<!DOCTYPE HTML>
<html lang="zh-CN">


<head>
    <meta charset="utf-8">
    <meta name="keywords" content="单元测试指南, 闪烁之狐, Blinkfox">
    <meta name="description" content="从来没有真正的绝境，只有心灵的迷途">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <meta name="renderer" content="webkit|ie-stand|ie-comp">
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="format-detection" content="telephone=no">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
    <!-- Global site tag (gtag.js) - Google Analytics -->


    <title>单元测试指南 | 闪烁之狐</title>
    <link rel="icon" type="image/png" href="/favicon.png">

    <link rel="stylesheet" type="text/css" href="/libs/awesome/css/all.css">
    <link rel="stylesheet" type="text/css" href="/libs/materialize/materialize.min.css">
    <link rel="stylesheet" type="text/css" href="/libs/aos/aos.css">
    <link rel="stylesheet" type="text/css" href="/libs/animate/animate.min.css">
    <link rel="stylesheet" type="text/css" href="/libs/lightGallery/css/lightgallery.min.css">
    <link rel="stylesheet" type="text/css" href="/css/matery.css">
    <link rel="stylesheet" type="text/css" href="/css/my.css">

    <script src="/libs/jquery/jquery.min.js"></script>

<meta name="generator" content="Hexo 5.2.0"><link rel="alternate" href="/atom.xml" title="闪烁之狐" type="application/atom+xml">
</head>




<body>
    <header class="navbar-fixed">
    <nav id="headNav" class="bg-color nav-transparent">
        <div id="navContainer" class="nav-wrapper container">
            <div class="brand-logo">
                <a href="/" class="waves-effect waves-light">
                    
                    <img src="/medias/logo.png" class="logo-img" alt="LOGO">
                    
                    <span class="logo-span">闪烁之狐</span>
                </a>
            </div>
            

<a href="#" data-target="mobile-nav" class="sidenav-trigger button-collapse"><i class="fas fa-bars"></i></a>
<ul class="right nav-menu">
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/" class="waves-effect waves-light">
      
      <i class="fas fa-home" style="zoom: 0.6;"></i>
      
      <span>首页</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/tags" class="waves-effect waves-light">
      
      <i class="fas fa-tags" style="zoom: 0.6;"></i>
      
      <span>标签</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/categories" class="waves-effect waves-light">
      
      <i class="fas fa-bookmark" style="zoom: 0.6;"></i>
      
      <span>分类</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/archives" class="waves-effect waves-light">
      
      <i class="fas fa-archive" style="zoom: 0.6;"></i>
      
      <span>归档</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/about" class="waves-effect waves-light">
      
      <i class="fas fa-user-circle-o" style="zoom: 0.6;"></i>
      
      <span>关于</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/friends" class="waves-effect waves-light">
      
      <i class="fas fa-address-book" style="zoom: 0.6;"></i>
      
      <span>友情链接</span>
    </a>
    
  </li>
  
  <li>
    <a href="#searchModal" class="modal-trigger waves-effect waves-light">
      <i id="searchIcon" class="fas fa-search" title="搜索" style="zoom: 0.85;"></i>
    </a>
  </li>
</ul>


<div id="mobile-nav" class="side-nav sidenav">

    <div class="mobile-head bg-color">
        
        <img src="/medias/logo.png" class="logo-img circle responsive-img">
        
        <div class="logo-name">闪烁之狐</div>
        <div class="logo-desc">
            
            从来没有真正的绝境，只有心灵的迷途
            
        </div>
    </div>

    

    <ul class="menu-list mobile-menu-list">
        
        <li class="m-nav-item">
	  
		<a href="/" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-home"></i>
			
			首页
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/tags" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-tags"></i>
			
			标签
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/categories" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-bookmark"></i>
			
			分类
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/archives" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-archive"></i>
			
			归档
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/about" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-user-circle-o"></i>
			
			关于
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/friends" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-address-book"></i>
			
			友情链接
		</a>
          
        </li>
        
        
        <li><div class="divider"></div></li>
        <li>
            <a href="https://github.com/blinkfox/hexo-theme-matery" class="waves-effect waves-light" target="_blank">
                <i class="fab fa-github-square fa-fw"></i>Fork Me
            </a>
        </li>
        
    </ul>
</div>


        </div>

        
            <style>
    .nav-transparent .github-corner {
        display: none !important;
    }

    .github-corner {
        position: absolute;
        z-index: 10;
        top: 0;
        right: 0;
        border: 0;
        transform: scale(1.1);
    }

    .github-corner svg {
        color: #0f9d58;
        fill: #fff;
        height: 64px;
        width: 64px;
    }

    .github-corner:hover .octo-arm {
        animation: a 0.56s ease-in-out;
    }

    .github-corner .octo-arm {
        animation: none;
    }

    @keyframes a {
        0%,
        to {
            transform: rotate(0);
        }
        20%,
        60% {
            transform: rotate(-25deg);
        }
        40%,
        80% {
            transform: rotate(10deg);
        }
    }
</style>

<a href="https://github.com/blinkfox/hexo-theme-matery" class="github-corner tooltipped hide-on-med-and-down" target="_blank"
   data-tooltip="Fork Me" data-position="left" data-delay="50">
    <svg viewBox="0 0 250 250" aria-hidden="true">
        <path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
        <path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
              fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>
        <path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
              fill="currentColor" class="octo-body"></path>
    </svg>
</a>
        
    </nav>

</header>

    



<div class="bg-cover pd-header post-cover" style="background-image: url('https://statics.sh1a.qingstor.com/2018/11/15/unit-test.jpg')">
    <div class="container" style="right: 0px;left: 0px;">
        <div class="row">
            <div class="col s12 m12 l12">
                <div class="brand">
                    <h1 class="description center-align post-title">单元测试指南</h1>
                </div>
            </div>
        </div>
    </div>
</div>




<main class="post-container content">

    
    <link rel="stylesheet" href="/libs/tocbot/tocbot.css">
<style>
    #articleContent h1::before,
    #articleContent h2::before,
    #articleContent h3::before,
    #articleContent h4::before,
    #articleContent h5::before,
    #articleContent h6::before {
        display: block;
        content: " ";
        height: 100px;
        margin-top: -100px;
        visibility: hidden;
    }

    #articleContent :focus {
        outline: none;
    }

    .toc-fixed {
        position: fixed;
        top: 64px;
    }

    .toc-widget {
        width: 345px;
        padding-left: 20px;
    }

    .toc-widget .toc-title {
        padding: 35px 0 15px 17px;
        font-size: 1.5rem;
        font-weight: bold;
        line-height: 1.5rem;
    }

    .toc-widget ol {
        padding: 0;
        list-style: none;
    }

    #toc-content {
        padding-bottom: 30px;
        overflow: auto;
    }

    #toc-content ol {
        padding-left: 10px;
    }

    #toc-content ol li {
        padding-left: 10px;
    }

    #toc-content .toc-link:hover {
        color: #42b983;
        font-weight: 700;
        text-decoration: underline;
    }

    #toc-content .toc-link::before {
        background-color: transparent;
        max-height: 25px;

        position: absolute;
        right: 23.5vw;
        display: block;
    }

    #toc-content .is-active-link {
        color: #42b983;
    }

    #floating-toc-btn {
        position: fixed;
        right: 15px;
        bottom: 76px;
        padding-top: 15px;
        margin-bottom: 0;
        z-index: 998;
    }

    #floating-toc-btn .btn-floating {
        width: 48px;
        height: 48px;
    }

    #floating-toc-btn .btn-floating i {
        line-height: 48px;
        font-size: 1.4rem;
    }
</style>
<div class="row">
    <div id="main-content" class="col s12 m12 l9">
        <!-- 文章内容详情 -->
<div id="artDetail">
    <div class="card">
        <div class="card-content article-info">
            <div class="row tag-cate">
                <div class="col s7">
                    
                    <div class="article-tag">
                        
                            <a href="/tags/Java/">
                                <span class="chip bg-color">Java</span>
                            </a>
                        
                            <a href="/tags/%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/">
                                <span class="chip bg-color">单元测试</span>
                            </a>
                        
                    </div>
                    
                </div>
                <div class="col s5 right-align">
                    
                    <div class="post-cate">
                        <i class="fas fa-bookmark fa-fw icon-category"></i>
                        
                            <a href="/categories/%E5%90%8E%E7%AB%AF/" class="post-category">
                                后端
                            </a>
                        
                    </div>
                    
                </div>
            </div>

            <div class="post-info">
                
                <div class="post-date info-break-policy">
                    <i class="far fa-calendar-minus fa-fw"></i>发布日期:&nbsp;&nbsp;
                    2018-11-15
                </div>
                

                

                
                <div class="info-break-policy">
                    <i class="far fa-file-word fa-fw"></i>文章字数:&nbsp;&nbsp;
                    11.8k
                </div>
                

                
                <div class="info-break-policy">
                    <i class="far fa-clock fa-fw"></i>阅读时长:&nbsp;&nbsp;
                    46 分
                </div>
                

                
                    <div id="busuanzi_container_page_pv" class="info-break-policy">
                        <i class="far fa-eye fa-fw"></i>阅读次数:&nbsp;&nbsp;
                        <span id="busuanzi_value_page_pv"></span>
                    </div>
				
            </div>
        </div>
        <hr class="clearfix">

        <link rel="stylesheet" href="/libs/prism/prism.css">
        <!-- 代码块折行 -->
        

        <div class="card-content article-card-content">
            <div id="articleContent">
                <h2 id="一、必要性"><a href="#一、必要性" class="headerlink" title="一、必要性"></a>一、必要性</h2><p>在我们公司中要做单元测试，确实比较难，因为公司缺少这种氛围，有也只是局部的，大多数工程师没有这方面的习惯和素养，很多人都是有一定的抵触的心理，经过我私下的了解大概有以下几种原因吧。</p>
<ul>
<li>写单元测试太<strong>耗费时间</strong>了，项目要赶进度，编写单元测试会导致不能按时完成开发任务，<strong>导致项目延期</strong>；</li>
<li>做<em>传统xx管理系统</em>的项目，业务逻辑比较简单，主要就是对业务数据做<strong>增删改查</strong>，单元测试意义和价值不高；</li>
<li>公司有专门的测试人员，很多问题在集成测试时一定能发现。</li>
<li>以前项目上从没写过单元测试，没有经验，不知道怎么编写单元测试；</li>
</ul>
<p>这其中对单元测试就有些误解了，单元测试有几个比较常见的典型场景：</p>
<ul>
<li>开发前写单元测试，通过测试描述需求，即<a target="_blank" rel="noopener" href="https://www.ibm.com/developerworks/cn/linux/l-tdd/">测试驱动开发</a>。</li>
<li>在开发过程中及时得到反馈，提前规避隐患和发现问题。</li>
<li>应用于自动化构建或持续集成流程，对每次代码修改做回归测试。</li>
<li>作为重构的基础，验证重构是否可靠。</li>
</ul>
<p>还有最重要的一点：<strong>编写单元测试的难易程度能够直接反应出代码的设计水平，能写出单元测试和写不出单元测试之间体现了编程能力上的巨大的鸿沟。无论是什么样的程序员，坚持编写一段时间的单元测试之后，都会明显感受到代码设计能力的巨大提升</strong>。</p>
<p>公司开发人员的代码质量往往不是很高，尤其是对代码的拆分和逻辑的抽象还处于懵懂阶段。要对这类代码写单测，即使是工作了3，4年的高级码农也是一个挑战，对新人来说几乎是不可能完成的任务。这也让很多开发人员有了<strong>写单元测试很难</strong>的感觉。所以，<strong>写单元测试的难易程度跟代码的质量关系最大，并且是决定性的</strong>。项目里无论用了哪个测试框架都不能解决代码本身难以测试的问题。</p>
<p>诚然，写单元测试在开发期间的确是会耗费更多时间的，尤其是要追求很高(超过<code>80%</code>，甚至<code>100%</code>)的代码覆盖率，更是需要耗费大量心血才能达到的。对于一些只需一次交付，很少维护的项目来说，意义和价值确实不是很大。但这本质上是属于为了赚快钱，不负责任的行为了，毕竟谁都无法保障自己写的程序，真的没有丝毫问题。这个问题的出现并不是个人的问题，而是反映了公司项目管理中的问题。当然，个人的原因也存在，就是如何在有限的时间里，提高效率。</p>
<p>目前公司的大多数项目其实都有着至少两年的维护时间的，很多开发人员都不愿意把自己的时间耗在一个代码很烂、没有单元测试保障且经常变更需求的项目里面。总之，包括我本人在内，都是有项目<strong>维护恐惧症</strong>的，更愿意投入到新项目的开发中。但是新项目里面还是没有单元测试的保障，代码质量逐渐低劣，如此就又形成了一个不断的循环之中。无法挣脱这个循环的人员就只能选择离职了，也许不慎又到了新的漩涡里面。</p>
<blockquote>
<p>一个 bug 被隐藏的时间越长，修复这个 bug 的代价就越大。</p>
</blockquote>
<p>单元测试能帮助我们在早期就规避、发现和修复很多不易察觉的 bug 和漏洞，而且更能保障后期的需求变动和代码重构时所带来的隐患，减少测试成本和维护成本。所以，在新项目中逐步推广和编写单元测试是有必要的，这将大大提高项目中代码的质量和可靠性，有些老项目中就算了吧，往往维护人员的负面情绪可能会更多，一些新的功能特性倒是可以试试。虽然写好单元测试很难，但<strong>写单元测试的难度其实是小于决定写单元测试的勇气的</strong>。</p>
<h2 id="二、基本概念"><a href="#二、基本概念" class="headerlink" title="二、基本概念"></a>二、基本概念</h2><p><strong>单元测试</strong>：单元测试又称模块测试，属于白盒测试，是最小单位的测试。模块分为程序模块和功能模块。功能模块指实现了一个完整功能的模块（单元），一个完整的程序单元具备输入、加工和输出三个环节。而且每个程序单元都应该有正规的规格说明，使之对其输入、加工和输出的关系做出名明确的描述。</p>
<p><strong>驱动测试</strong>：驱动被测试模块正常运行起来的实体。通俗的说法就是你负责测试模块/方法是中间的，没有<code>main()</code>方法入口，怎么编译，怎么启动呢？就需要写一个带<code>main()</code>的方法来调用你的模块/方法，这个就是驱动测试。</p>
<p><strong>测试桩</strong>：代替被测模块调用的子模块的实体，该实体一般为桩函数（stub）。通俗的说法就是你负责测试的模块/方法所调用的模块/方法，所以你需要模仿他们做一个返回值（假的，但符合设计）。</p>
<p><strong>测试覆盖</strong>：评测测试过程中已经执行的代码的多少。</p>
<p><strong>测试覆盖率</strong>：代码的覆盖程度，一种度量方式。针对代码的测试覆盖率有很多种度量方式，常见的有以下几种:</p>
<ul>
<li>语句覆盖</li>
<li>判定覆盖</li>
<li>路径覆盖</li>
</ul>
<p>测试覆盖率数据到底有多大意义。主要有以下几个观点：</p>
<ul>
<li>路径覆盖率 &gt; 判定覆盖 &gt; 语句覆盖</li>
<li>覆盖率数据只能代表你测试过哪些代码，不能代表你是否测试好这些代码。</li>
<li>不要过于相信覆盖率数据，100%的测试覆盖率并不能保证bug的不出现。</li>
<li>代码覆盖率只是一个最基本的前提，一定要保证，但不是意味着达到指标就代表测试的完成</li>
<li>测试人员不能盲目追求代码覆盖率，而应该想办法设计更多更好的案例，哪怕多设计出来的案例对覆盖率一点影响也没有。</li>
</ul>
<h2 id="三、单元测试工具"><a href="#三、单元测试工具" class="headerlink" title="三、单元测试工具"></a>三、单元测试工具</h2><p>在Java中有非常多的单元测试的工具或框架可供选择，我这里只选择一些常用的、主流的单元测试框架或者工具来作介绍和使用。</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://junit.org/junit5/">JUnit</a>：Java中最有名、使用最广泛的单元测试框架</li>
<li><a target="_blank" rel="noopener" href="http://site.mockito.org/">Mockito</a>：模拟框架，可以让你用干净而简单的API编写测试</li>
<li><a target="_blank" rel="noopener" href="https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html">Spring Test</a>: 使用 Spring Test 来对Spring相关的项目做单元测试，其中会结合或者集成其他测试框架和工具</li>
<li><a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/2.0.2.RELEASE/reference/htmlsingle/#boot-features-testing">spring-boot-starter-test</a>: SpringBoot项目中的单元测试</li>
<li><a target="_blank" rel="noopener" href="https://www.eclemma.org/jacoco/trunk/index.html">JaCoCo</a>: 使用离线和运行时字节码工具来收集代码覆盖率指标的框架。</li>
</ul>
<h3 id="1-JUnit4"><a href="#1-JUnit4" class="headerlink" title="1. JUnit4"></a>1. JUnit4</h3><p>JUnit 是使用 Java 语言编写的用于编写和运行可重复的自动化测试的开源测试框架。除了 Junit 之外，<a target="_blank" rel="noopener" href="http://testng.org/doc/">TestNg</a>也是Java中非常受欢迎的单元测试框架。两种框架在功能上看起来非常相似，这里有一篇关于<a target="_blank" rel="noopener" href="https://www.mkyong.com/unittest/junit-4-vs-testng-comparison/">JUnit 4 与 TestNG 的对比</a>，还有一篇较为全面的介绍<a target="_blank" rel="noopener" href="https://www.guru99.com/all-about-testng-and-selenium.html">TestNG的教程</a>，总体来说，TestNG 比 Junit4 功能更强大一些，但是相比 Junit5 而言，TestNG 又落后了一代。开源的轮子滚滚向前，都是一代新的轮子超越一代老的轮子。所以，我们这里就只选择 Junit 来作单元测试框架的介绍了吧。</p>
<p><img src="https://statics.sh1a.qingstor.com/2018/11/15/junitvstestng.jpg" alt="JUnit4 和 TestNG 的功能比较"></p>
<p>目前最新版本是 JUnit5.2.0，相比 JUnit4 而言有很大的改变，这里主要讲解 JUnit4 的使用(目前的新老项目中应该使用的更多)，并对 JUnit5 做简要介绍。学习了 Junit4 的主要使用方式之后，大家再去看<a target="_blank" rel="noopener" href="http://sjyuan.cc/junit5/user-guide-cn/">JUnit5 用户指南</a>在将来逐渐使用起来更好些。</p>
<h4 id="1-简单示例"><a href="#1-简单示例" class="headerlink" title="(1). 简单示例"></a>(1). 简单示例</h4><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">import</span> <span class="token keyword">static</span> <span class="token namespace">org<span class="token punctuation">.</span>junit<span class="token punctuation">.</span></span><span class="token class-name">Assert</span><span class="token punctuation">.</span>*<span class="token punctuation">;</span>

<span class="token keyword">import</span> <span class="token namespace">org<span class="token punctuation">.</span>junit<span class="token punctuation">.</span></span><span class="token class-name">Test</span><span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">CalculateTest</span> <span class="token punctuation">&#123;</span>

    <span class="token annotation punctuation">@Test</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">testSum</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token class-name">Calculate</span> calculation <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Calculate</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">int</span> sum <span class="token operator">=</span> calculation<span class="token punctuation">.</span><span class="token function">sum</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">int</span> testSum <span class="token operator">=</span> <span class="token number">7</span><span class="token punctuation">;</span>

        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"@Test sum(): "</span> <span class="token operator">+</span> sum <span class="token operator">+</span> <span class="token string">" = "</span> <span class="token operator">+</span> testSum<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">assertEquals</span><span class="token punctuation">(</span>sum<span class="token punctuation">,</span> testSum<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<h4 id="2-注解"><a href="#2-注解" class="headerlink" title="(2). 注解"></a>(2). 注解</h4><ul>
<li><code>@Test</code>: 测试方法，在这里还可以测试<strong>期望异常</strong>和<strong>超时时间</strong>。</li>
<li><code>@Before</code>: 每个测试方法执行之前执行的方法。</li>
<li><code>@BeforeClass</code>: 一个测试类中所有测试方法执行之前执行的方法，只执行一次，且方法必须为<code>static</code>的。</li>
<li><code>@After</code>: 每个测试方法执行之后执行的方法。</li>
<li><code>@AfterClass</code>: 一个测试类中所有测试方法执行之后执行的方法，只执行一次，且方法必须为<code>static</code>的。</li>
<li><code>@Ignore</code>: 忽略的测试方法。</li>
<li><code>@RunWith</code>: 指定测试类使用某个运行器。</li>
<li><code>@Parameters</code>: 参数化测试，指定测试类的测试数据集合。</li>
<li><code>@FixMethodOrder</code>: 注解在测试类上指定测试方法按一定顺序规则来执行，有三种。</li>
</ul>
<p>一个测试类单元测试的执行顺序为：</p>
<blockquote>
<p>@BeforeClass –&gt; @Before –&gt; @Test –&gt; @After –&gt; @AfterClass</p>
</blockquote>
<p>每一个测试方法的执行顺序为：</p>
<blockquote>
<p>@Before –&gt; @Test –&gt; @After</p>
</blockquote>
<p>综合示例：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">import</span> <span class="token keyword">static</span> <span class="token namespace">org<span class="token punctuation">.</span>junit<span class="token punctuation">.</span></span><span class="token class-name">Assert</span><span class="token punctuation">.</span>*<span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span></span><span class="token operator">*</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token namespace">org<span class="token punctuation">.</span>junit<span class="token punctuation">.</span></span><span class="token operator">*</span><span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">AnnotationsTest</span> <span class="token punctuation">&#123;</span>

    <span class="token keyword">private</span> <span class="token class-name">ArrayList</span> testList<span class="token punctuation">;</span>

    <span class="token annotation punctuation">@BeforeClass</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">onceExecutedBeforeAll</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"@BeforeClass: onceExecutedBeforeAll"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token annotation punctuation">@Before</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">executedBeforeEach</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        testList <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"@Before: executedBeforeEach"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token annotation punctuation">@AfterClass</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">onceExecutedAfterAll</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"@AfterClass: onceExecutedAfterAll"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token annotation punctuation">@After</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">executedAfterEach</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        testList<span class="token punctuation">.</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"@After: executedAfterEach"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token annotation punctuation">@Test</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token class-name">EmptyCollection</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token function">assertTrue</span><span class="token punctuation">(</span>testList<span class="token punctuation">.</span><span class="token function">isEmpty</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"@Test: EmptyArrayList"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token punctuation">&#125;</span>

    <span class="token annotation punctuation">@Test</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token class-name">OneItemCollection</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        testList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"oneItem"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">assertEquals</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> testList<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"@Test: OneItemArrayList"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token annotation punctuation">@Ignore</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">executionIgnored</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"@Ignore: This execution is ignored"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>如果我们运行上面的测试，控制台输出将是以下几点：</p>
<pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">@BeforeClass: onceExecutedBeforeAll
@Before: executedBeforeEach
@Test: EmptyArrayList
@After: executedAfterEach
@Before: executedBeforeEach
@Test: OneItemArrayList
@After: executedAfterEach
@AfterClass: onceExecutedAfterAll<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<h4 id="3-断言"><a href="#3-断言" class="headerlink" title="(3). 断言"></a>(3). 断言</h4><p>断言是编写测试用例的核心实现方式，即期望值是多少，测试的结果是多少，以此来判断测试是否通过。JUnit4.x中的断言核心方法如下：</p>
<ul>
<li><code>assertArrayEquals(expecteds, actuals)</code>: 查看两个数组是否相等。</li>
<li><code>assertEquals(expected, actual)</code>: 查看两个对象是否相等。类似于字符串比较使用的equals()方法。</li>
<li><code>assertNotEquals(first, second)</code>: 查看两个对象是否不相等。</li>
<li><code>assertNull(object)</code>: 查看对象是否为空。</li>
<li><code>assertNotNull(object)</code>: 查看对象是否不为空。</li>
<li><code>assertSame(expected, actual)</code>: 查看两个对象的引用是否相等。类似于使用“==”比较两个对象。</li>
<li><code>assertNotSame(unexpected, actual)</code>: 查看两个对象的引用是否不相等。类似于使用“!=”比较两个对象。</li>
<li><code>assertTrue(condition)</code>: 查看运行结果是否为true。</li>
<li><code>assertFalse(condition)</code>: 查看运行结果是否为false。</li>
<li><code>assertThat(actual, matcher)</code>: 查看实际值是否满足指定的条件。</li>
<li><code>fail()</code>: 让测试失败。</li>
</ul>
<h4 id="4-套件测试"><a href="#4-套件测试" class="headerlink" title="(4). 套件测试"></a>(4). 套件测试</h4><p>测试套件意味着捆绑几个单元测试用例并且一起执行他们。在 JUnit 中，<code>@RunWith</code>和<code>@Suite</code>注释用来运行套件测试。简单示例如下：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">TestJunit1</span> <span class="token punctuation">&#123;</span>

   <span class="token annotation punctuation">@Test</span>
   <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">testPrint1</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
      <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"Test Junit 1..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
   <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">TestJunit2</span> <span class="token punctuation">&#123;</span>

   <span class="token annotation punctuation">@Test</span>
   <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">testPrint2</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
      <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"Test Junit 2..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
   <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@RunWith</span><span class="token punctuation">(</span><span class="token class-name">Suite</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span>
<span class="token annotation punctuation">@Suite</span><span class="token punctuation">.</span><span class="token class-name">SuiteClasses</span><span class="token punctuation">(</span><span class="token punctuation">&#123;</span>
   <span class="token class-name">TestJunit1</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span>
   <span class="token class-name">TestJunit2</span><span class="token punctuation">.</span><span class="token keyword">class</span>
<span class="token punctuation">&#125;</span><span class="token punctuation">)</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">JunitTestSuite</span> <span class="token punctuation">&#123;</span>

<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<h4 id="5-参数化测试"><a href="#5-参数化测试" class="headerlink" title="(5). 参数化测试"></a>(5). 参数化测试</h4><p>一个测试类也可以被看作是一个参数化测试类。但它要满足下列所有要求：</p>
<ul>
<li>该类被注解为<code>@RunWith(Parameterized.class)</code>。</li>
<li>这个类有一个构造函数，存储测试数据。</li>
<li>这个类有一个静态方法生成并返回测试数据，并注明<code>@Parameters</code>注解。</li>
<li>这个类有一个测试，它需要注解<code>@Test</code>到方法。</li>
</ul>
<p>简单示例如下：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">import</span> <span class="token keyword">static</span> <span class="token namespace">org<span class="token punctuation">.</span>junit<span class="token punctuation">.</span></span><span class="token class-name">Assert</span><span class="token punctuation">.</span>assertEquals<span class="token punctuation">;</span>

<span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span></span><span class="token class-name">Arrays</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token namespace">java<span class="token punctuation">.</span>util<span class="token punctuation">.</span></span><span class="token class-name">Collection</span><span class="token punctuation">;</span>

<span class="token keyword">import</span> <span class="token namespace">org<span class="token punctuation">.</span>junit<span class="token punctuation">.</span></span><span class="token class-name">Test</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token namespace">org<span class="token punctuation">.</span>junit<span class="token punctuation">.</span>runner<span class="token punctuation">.</span></span><span class="token class-name">RunWith</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token namespace">org<span class="token punctuation">.</span>junit<span class="token punctuation">.</span>runners<span class="token punctuation">.</span></span><span class="token class-name">Parameterized</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token namespace">org<span class="token punctuation">.</span>junit<span class="token punctuation">.</span>runners<span class="token punctuation">.</span></span><span class="token class-name">Parameterized</span><span class="token punctuation">.</span><span class="token class-name">Parameters</span><span class="token punctuation">;</span>

<span class="token annotation punctuation">@RunWith</span><span class="token punctuation">(</span><span class="token class-name">Parameterized</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">CalculateTest</span> <span class="token punctuation">&#123;</span>

    <span class="token keyword">private</span> <span class="token keyword">int</span> expected<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token keyword">int</span> first<span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token keyword">int</span> second<span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token class-name">CalculateTest</span><span class="token punctuation">(</span><span class="token keyword">int</span> expectedResult<span class="token punctuation">,</span> <span class="token keyword">int</span> firstNumber<span class="token punctuation">,</span> <span class="token keyword">int</span> secondNumber<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>expected <span class="token operator">=</span> expectedResult<span class="token punctuation">;</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>first <span class="token operator">=</span> firstNumber<span class="token punctuation">;</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>second <span class="token operator">=</span> secondNumber<span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token annotation punctuation">@Parameters</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token class-name">Collection</span> <span class="token function">addedNumbers</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">return</span> <span class="token class-name">Arrays</span><span class="token punctuation">.</span><span class="token function">asList</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Integer</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">&#123;</span> <span class="token punctuation">&#123;</span> <span class="token number">3</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span> <span class="token punctuation">&#125;</span><span class="token punctuation">,</span> <span class="token punctuation">&#123;</span> <span class="token number">5</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">3</span> <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
                <span class="token punctuation">&#123;</span> <span class="token number">7</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">,</span> <span class="token number">4</span> <span class="token punctuation">&#125;</span><span class="token punctuation">,</span> <span class="token punctuation">&#123;</span> <span class="token number">9</span><span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">,</span> <span class="token number">5</span> <span class="token punctuation">&#125;</span><span class="token punctuation">,</span> <span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token annotation punctuation">@Test</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">sum</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token class-name">Calculate</span> add <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Calculate</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"Addition with parameters : "</span> <span class="token operator">+</span> first <span class="token operator">+</span> <span class="token string">" and "</span> <span class="token operator">+</span> second<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">assertEquals</span><span class="token punctuation">(</span>expected<span class="token punctuation">,</span> add<span class="token punctuation">.</span><span class="token function">sum</span><span class="token punctuation">(</span>first<span class="token punctuation">,</span> second<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>运行<code>CalculateTest</code>测试用例，控制台输出如下：</p>
<pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">Addition with parameters <span class="token builtin class-name">:</span> <span class="token number">1</span> and <span class="token number">2</span>
Adding values: <span class="token number">1</span> + <span class="token number">2</span>
Addition with parameters <span class="token builtin class-name">:</span> <span class="token number">2</span> and <span class="token number">3</span>
Adding values: <span class="token number">2</span> + <span class="token number">3</span>
Addition with parameters <span class="token builtin class-name">:</span> <span class="token number">3</span> and <span class="token number">4</span>
Adding values: <span class="token number">3</span> + <span class="token number">4</span>
Addition with parameters <span class="token builtin class-name">:</span> <span class="token number">4</span> and <span class="token number">5</span>
Adding values: <span class="token number">4</span> + <span class="token number">5</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<h4 id="6-忽略测试"><a href="#6-忽略测试" class="headerlink" title="(6). 忽略测试"></a>(6). 忽略测试</h4><p>有时可能会发生我们的代码还没有准备好的情况，这时测试用例去测试这个方法或代码的时候会造成失败。<code>@Ignore</code>注释会在这种情况时帮助我们。</p>
<ul>
<li>一个含有<code>@Ignore</code>注释的测试方法将不会被执行。</li>
<li>如果一个测试类有<code>@Ignore</code>注释，则它的测试方法将不会执行</li>
</ul>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">JunitTest3</span> <span class="token punctuation">&#123;</span>

    <span class="token annotation punctuation">@Test</span>
    <span class="token annotation punctuation">@Ignore</span><span class="token punctuation">(</span><span class="token string">"该测试方法还没准备好运行."</span><span class="token punctuation">)</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">testHello</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"Hello World!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>在上面的示例中，JUnit将不会执行<code>testHello()</code>方法。</p>
<h4 id="7-异常测试"><a href="#7-异常测试" class="headerlink" title="(7). 异常测试"></a>(7). 异常测试</h4><p>它用于测试由方法抛出的异常。</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">import</span> <span class="token namespace">org<span class="token punctuation">.</span>junit<span class="token punctuation">.</span></span><span class="token operator">*</span><span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">JunitTest4</span> <span class="token punctuation">&#123;</span>

    <span class="token annotation punctuation">@Test</span><span class="token punctuation">(</span>expected <span class="token operator">=</span> <span class="token class-name">ArithmeticException</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">testWithException</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
      <span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">1</span> <span class="token operator">/</span> <span class="token number">0</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>在上面的示例中，<code>testWithException()</code>方法将抛出<code>ArithmeticException</code>异常，因为这是一个预期的异常，因此单元测试会通过。</p>
<h4 id="8-超时测试"><a href="#8-超时测试" class="headerlink" title="(8). 超时测试"></a>(8). 超时测试</h4><p><strong>超时测试</strong>是指，一个单元测试运行时间是否超过指定的毫秒数，测试将终止并标记为失败。</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">import</span> <span class="token namespace">org<span class="token punctuation">.</span>junit<span class="token punctuation">.</span></span><span class="token operator">*</span><span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">JunitTest5</span> <span class="token punctuation">&#123;</span>

    <span class="token annotation punctuation">@Test</span><span class="token punctuation">(</span>timeout <span class="token operator">=</span> <span class="token number">1000</span><span class="token punctuation">)</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">testTimeout</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            <span class="token comment">// do nothing.</span>
        <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span>

<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>在上面的示例中，<code>testTimeout()</code>方法将不会返回，因此JUnit引擎会将其标记为失败，并抛出一个异常。<code>java.lang.Exception:test timed out after 1000 milliseconds</code>。</p>
<h4 id="9-Hamcrest"><a href="#9-Hamcrest" class="headerlink" title="(9). Hamcrest"></a>(9). Hamcrest</h4><p>在实际开发中，一些基本的断言，如<code>eqaul</code>, <code>null</code>, <code>true</code>它们的可读性并不是很好。而且很多时候我们要比较对象、集合、Map等数据结构。这样我们要么进行大段的字段获取再断言。或者干脆自己编写表达式并断言其结果。JUnit4.4 引入了 Hamcrest 框架，Hamcest 提供了一套匹配符 Matcher，这些匹配符更接近自然语言，可读性高，更加灵活。</p>
<p><a target="_blank" rel="noopener" href="http://hamcrest.org/">Hamcrest</a>提供了大量被称为“匹配器”的方法。其中每个匹配器都设计用于执行特定的比较操作。Hamcrest 的可扩展性很好，让你能够创建自定义的匹配器。最重要的是，JUnit 也包含了 Hamcrest 的核心，提供了对 Hamcrest 的原生支持，可以直接使用 Hamcrest。当然要使用功能齐备的Hamcrest，还是要引入对它的依赖。</p>
<p>看个对比例子，前者使用Junit的 断言，后者使用 Hamcrest 的断言。</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@Test</span>
<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">test_with_junit_assert</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">int</span> expected <span class="token operator">=</span> <span class="token number">51</span><span class="token punctuation">;</span>
    <span class="token keyword">int</span> actual <span class="token operator">=</span> <span class="token number">51</span><span class="token punctuation">;</span>

    <span class="token function">assertEquals</span><span class="token punctuation">(</span><span class="token string">"failure - They are not same!"</span><span class="token punctuation">,</span> expected<span class="token punctuation">,</span> actual<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span>

<span class="token annotation punctuation">@Test</span>
<span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">test_with_hamcrest_assertThat</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">int</span> expected <span class="token operator">=</span> <span class="token number">51</span><span class="token punctuation">;</span>
    <span class="token keyword">int</span> actual <span class="token operator">=</span> <span class="token number">51</span><span class="token punctuation">;</span>

    <span class="token function">assertThat</span><span class="token punctuation">(</span><span class="token string">"failure - They are not same!"</span><span class="token punctuation">,</span> actual<span class="token punctuation">,</span> <span class="token function">equalTo</span><span class="token punctuation">(</span>expected<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// 联合匹配符not和equalTo表示“不等于”</span>
<span class="token function">assertThat</span><span class="token punctuation">(</span> something<span class="token punctuation">,</span> <span class="token function">not</span><span class="token punctuation">(</span> <span class="token function">equalTo</span><span class="token punctuation">(</span> <span class="token string">"developer"</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> 
<span class="token comment">// 联合匹配符not和containsString表示“不包含子字符串”</span>
<span class="token function">assertThat</span><span class="token punctuation">(</span> something<span class="token punctuation">,</span> <span class="token function">not</span><span class="token punctuation">(</span> <span class="token function">containsString</span><span class="token punctuation">(</span> <span class="token string">"Works"</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> 
<span class="token comment">// 联合匹配符anyOf和containsString表示“包含任何一个子字符串”</span>
<span class="token function">assertThat</span><span class="token punctuation">(</span>something<span class="token punctuation">,</span> <span class="token function">anyOf</span><span class="token punctuation">(</span><span class="token function">containsString</span><span class="token punctuation">(</span><span class="token string">"developer"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">containsString</span><span class="token punctuation">(</span><span class="token string">"Works"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>使用 assertThat 的优点：</p>
<ol>
<li>Hamcrest 一条 assertThat 即可以替代其他所有的 assertion 语句，这样可以在所有的单元测试中只使用一个断言方法，使得编写测试用例变得简单，代码风格变得统一，测试代码也更容易维护。</li>
<li>assertThat 使用了 Hamcrest 的 Matcher 匹配符，用户可以使用匹配符规定的匹配准则精确的指定一些想设定满足的条件，具有很强的易读性，而且使用起来更加灵活</li>
<li>assertThat 不再像 assertEquals 那样，使用比较难懂的“谓宾主”语法模式（如：<code>assertEquals(3, x);</code>），相反，assertThat 使用了类似于“主谓宾”的易读语法模式（如：<code>assertThat(x,is(3));</code>），使得代码更加直观、易读。</li>
<li>可以将这些 Matcher 匹配符联合起来灵活使用，达到更多目的。</li>
</ol>
<p>JUnit 4.4 自带了一些 Hamcrest 的匹配符 Matcher，但是只有有限的几个，在类<code>org.hamcrest.CoreMatchers</code>中定义，要想使用他们，必须导入包 <code>org.hamcrest.CoreMatchers.*</code>。</p>
<p>Hamcrest 提供了很强大的一些api 供我们进行测试断言。</p>
<pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">核心：
    anything - 总是匹配,如果你不关心测试下的对象是什么是有用的
    describedAs - 添加一个定制的失败表述装饰器
    is - 改进可读性装饰器 - 见下 “Sugar”
逻辑：
    allOf - 如果所有匹配器都匹配才匹配,像Java里的<span class="token operator">&amp;&amp;</span>
    anyOf - 如果任何匹配器匹配就匹配,像Java里的<span class="token operator">||</span>
    not - 如果包装的匹配器不匹配器时匹配,反之亦然
对象：
    equalTo - 测试对象相等使用Object.equals方法
    hasToString - 测试Object.toString方法
    instanceOf, isCompatibleType - 测试类型
    notNullValue, nullValue - 测试null
    sameInstance - 测试对象实例
Beans：
    hasProperty - 测试JavaBeans属性
集合：
    array - 测试一个数组元素test an array’s elements against an array of matchers
    hasEntry, hasKey, hasValue - 测试一个Map包含一个实体,键或者值
    hasItem, hasItems - 测试一个集合包含一个元素
    hasItemInArray - 测试一个数组包含一个元素
数字：
    closeTo - 测试浮点值接近给定的值
    greaterThan, greaterThanOrEqualTo, lessThan, lessThanOrEqualTo - 测试次序
文本：
    equalToIgnoringCase - 测试字符串相等忽略大小写
    equalToIgnoringWhiteSpace - 测试字符串忽略空白
    containsString, endsWith, startsWith - 测试字符串匹配<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>以下示例代码列举了大部分 assertThat 的使用例子，供大家学习使用时参考：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">//---------------- 字符相关匹配符 ----------------</span>
<span class="token comment">/**equalTo匹配符断言被测的testedValue等于expectedValue，
* equalTo可以断言数值之间，字符串之间和对象之间是否相等，相当于Object的equals方法
*/</span>
<span class="token function">assertThat</span><span class="token punctuation">(</span>testedValue<span class="token punctuation">,</span> <span class="token function">equalTo</span><span class="token punctuation">(</span>expectedValue<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**equalToIgnoringCase匹配符断言被测的字符串testedString
*在忽略大小写的情况下等于expectedString
*/</span>
<span class="token function">assertThat</span><span class="token punctuation">(</span>testedString<span class="token punctuation">,</span> <span class="token function">equalToIgnoringCase</span><span class="token punctuation">(</span>expectedString<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**equalToIgnoringWhiteSpace匹配符断言被测的字符串testedString
*在忽略头尾的任意个空格的情况下等于expectedString，
*注意：字符串中的空格不能被忽略
*/</span>
<span class="token function">assertThat</span><span class="token punctuation">(</span>testedString<span class="token punctuation">,</span> <span class="token function">equalToIgnoringWhiteSpace</span><span class="token punctuation">(</span>expectedString<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**containsString匹配符断言被测的字符串testedString包含子字符串subString**/</span>
<span class="token function">assertThat</span><span class="token punctuation">(</span>testedString<span class="token punctuation">,</span> <span class="token function">containsString</span><span class="token punctuation">(</span>subString<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**endsWith匹配符断言被测的字符串testedString以子字符串suffix结尾*/</span>
<span class="token function">assertThat</span><span class="token punctuation">(</span>testedString<span class="token punctuation">,</span> <span class="token function">endsWith</span><span class="token punctuation">(</span>suffix<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**startsWith匹配符断言被测的字符串testedString以子字符串prefix开始*/</span>
<span class="token function">assertThat</span><span class="token punctuation">(</span>testedString<span class="token punctuation">,</span> <span class="token function">startsWith</span><span class="token punctuation">(</span>prefix<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// ---------------- 一般匹配符 ----------------</span>
<span class="token comment">/**nullValue()匹配符断言被测object的值为null*/</span>
<span class="token function">assertThat</span><span class="token punctuation">(</span>object<span class="token punctuation">,</span><span class="token function">nullValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**notNullValue()匹配符断言被测object的值不为null*/</span>
<span class="token function">assertThat</span><span class="token punctuation">(</span>object<span class="token punctuation">,</span><span class="token function">notNullValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**is匹配符断言被测的object等于后面给出匹配表达式*/</span>
<span class="token function">assertThat</span><span class="token punctuation">(</span>testedString<span class="token punctuation">,</span> <span class="token function">is</span><span class="token punctuation">(</span><span class="token function">equalTo</span><span class="token punctuation">(</span>expectedValue<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**is匹配符简写应用之一，is(equalTo(x))的简写，断言testedValue等于expectedValue*/</span>
<span class="token function">assertThat</span><span class="token punctuation">(</span>testedValue<span class="token punctuation">,</span> <span class="token function">is</span><span class="token punctuation">(</span>expectedValue<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**is匹配符简写应用之二，is(instanceOf(SomeClass.class))的简写，
*断言testedObject为Cheddar的实例
*/</span>
<span class="token function">assertThat</span><span class="token punctuation">(</span>testedObject<span class="token punctuation">,</span> <span class="token function">is</span><span class="token punctuation">(</span><span class="token class-name">Cheddar</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**not匹配符和is匹配符正好相反，断言被测的object不等于后面给出的object*/</span>
<span class="token function">assertThat</span><span class="token punctuation">(</span>testedString<span class="token punctuation">,</span> <span class="token function">not</span><span class="token punctuation">(</span>expectedString<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**allOf匹配符断言符合所有条件，相当于“与”（&amp;&amp;）*/</span>
<span class="token function">assertThat</span><span class="token punctuation">(</span>testedNumber<span class="token punctuation">,</span> <span class="token function">allOf</span><span class="token punctuation">(</span><span class="token function">greaterThan</span><span class="token punctuation">(</span><span class="token number">8</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">lessThan</span><span class="token punctuation">(</span><span class="token number">16</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**anyOf匹配符断言符合条件之一，相当于“或”（||）*/</span>
<span class="token function">assertThat</span><span class="token punctuation">(</span>testedNumber<span class="token punctuation">,</span> <span class="token function">anyOf</span><span class="token punctuation">(</span><span class="token function">greaterThan</span><span class="token punctuation">(</span><span class="token number">16</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">lessThan</span><span class="token punctuation">(</span><span class="token number">8</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// ---------------- 数值相关匹配符 ----------------</span>
<span class="token comment">/**closeTo匹配符断言被测的浮点型数testedDouble在20.0¡À0.5范围之内*/</span>
<span class="token function">assertThat</span><span class="token punctuation">(</span>testedDouble<span class="token punctuation">,</span> <span class="token function">closeTo</span><span class="token punctuation">(</span><span class="token number">20.0</span><span class="token punctuation">,</span> <span class="token number">0.5</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**greaterThan匹配符断言被测的数值testedNumber大于16.0*/</span>
<span class="token function">assertThat</span><span class="token punctuation">(</span>testedNumber<span class="token punctuation">,</span> <span class="token function">greaterThan</span><span class="token punctuation">(</span><span class="token number">16.0</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/** lessThan匹配符断言被测的数值testedNumber小于16.0*/</span>
<span class="token function">assertThat</span><span class="token punctuation">(</span>testedNumber<span class="token punctuation">,</span> lessThan <span class="token punctuation">(</span><span class="token number">16.0</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/** greaterThanOrEqualTo匹配符断言被测的数值testedNumber大于等于16.0*/</span>
<span class="token function">assertThat</span><span class="token punctuation">(</span>testedNumber<span class="token punctuation">,</span> greaterThanOrEqualTo <span class="token punctuation">(</span><span class="token number">16.0</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/** lessThanOrEqualTo匹配符断言被测的testedNumber小于等于16.0*/</span>
<span class="token function">assertThat</span><span class="token punctuation">(</span>testedNumber<span class="token punctuation">,</span> lessThanOrEqualTo <span class="token punctuation">(</span><span class="token number">16.0</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// ---------------- 集合相关匹配符 ----------------</span>
<span class="token comment">/**hasEntry匹配符断言被测的Map对象mapObject含有一个键值为"key"对应元素值为"value"的Entry项*/</span>
<span class="token function">assertThat</span><span class="token punctuation">(</span>mapObject<span class="token punctuation">,</span> <span class="token function">hasEntry</span><span class="token punctuation">(</span><span class="token string">"key"</span><span class="token punctuation">,</span> <span class="token string">"value"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/**hasItem匹配符表明被测的迭代对象iterableObject含有元素element项则测试通过*/</span>
<span class="token function">assertThat</span><span class="token punctuation">(</span>iterableObject<span class="token punctuation">,</span> hasItem <span class="token punctuation">(</span>element<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/** hasKey匹配符断言被测的Map对象mapObject含有键值“key”*/</span>
<span class="token function">assertThat</span><span class="token punctuation">(</span>mapObject<span class="token punctuation">,</span> hasKey <span class="token punctuation">(</span><span class="token string">"key"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">/** hasValue匹配符断言被测的Map对象mapObject含有元素值value*/</span>
<span class="token function">assertThat</span><span class="token punctuation">(</span>mapObject<span class="token punctuation">,</span> <span class="token function">hasValue</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<h3 id="2-JUnit5"><a href="#2-JUnit5" class="headerlink" title="2. JUnit5"></a>2. JUnit5</h3><h4 id="1-Junit5简介"><a href="#1-Junit5简介" class="headerlink" title="(1). Junit5简介"></a>(1). Junit5简介</h4><p>JUnit 5 跟以前的JUnit版本不一样，它由几大不同的模块组成，这些模块分别来自三个不同的子项目。</p>
<blockquote>
<p>JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage</p>
</blockquote>
<p><code>JUnit Platform</code>是在JVM上 启动测试框架 的基础平台。它还定义了<code>TestEngine API</code>，该API可用于开发在平台上运行的测试框架。此外，平台还提供了一个从命令行或者 Gradle 和 Maven 插件来启动的 控制台启动器 ，它就好比一个 基于 JUnit4 的 Runner 在平台上运行任何<code>TestEngine</code>。</p>
<p><code>JUnit Jupiter</code>是一个组合体，它是由在JUnit 5中编写测试和扩展的新 编程模型 和 扩展模型 组成。另外，<code>Jupiter</code>子项目还提供了一个TestEngine，用于在平台上运行基于Jupiter的测试。</p>
<p><code>JUnit Vintage</code> 提供了一个<code>TestEngine</code>，用于在平台上运行基于JUnit 3和JUnit 4的测试。</p>
<p>JUnit 5需要<code>Java 8</code>（或更高）的运行时环境。不过，你仍然可以测试那些由老版本JDK编译的代码。</p>
<h4 id="2-简单示例"><a href="#2-简单示例" class="headerlink" title="(2). 简单示例"></a>(2). 简单示例</h4><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">import</span> <span class="token keyword">static</span> <span class="token namespace">org<span class="token punctuation">.</span>junit<span class="token punctuation">.</span>jupiter<span class="token punctuation">.</span>api<span class="token punctuation">.</span></span><span class="token class-name">Assertions</span><span class="token punctuation">.</span>assertEquals<span class="token punctuation">;</span>

<span class="token keyword">import</span> <span class="token namespace">org<span class="token punctuation">.</span>junit<span class="token punctuation">.</span>jupiter<span class="token punctuation">.</span>api<span class="token punctuation">.</span></span><span class="token class-name">Test</span><span class="token punctuation">;</span>

<span class="token keyword">class</span> <span class="token class-name">FirstJUnit5Tests</span> <span class="token punctuation">&#123;</span>

    <span class="token annotation punctuation">@Test</span>
    <span class="token keyword">void</span> <span class="token function">myFirstTest</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token function">assertEquals</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">1</span> <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>表面上来看，使用方式和 Junit4 差别不大，但是与 JUnit4 比较起来还是有些不同的。</p>
<ol>
<li>导入测试测试注解（<code>@Test</code>）和断言方法（<code>assertEquals</code>）的包路径不同。</li>
<li>不需要手动把测试和测试方法声明为<code>public</code>了。</li>
</ol>
<h4 id="3-注解"><a href="#3-注解" class="headerlink" title="(3). 注解"></a>(3). 注解</h4><p><code>JUnit Jupiter</code>支持使用下面表格中的注解来配置测试和扩展框架。</p>
<p>所有的核心注解都位于<code>junit-jupiter-api</code>模块的org.junit.jupiter.api`包中。</p>
<ul>
<li><code>@Test</code>: 表示该方法是一个测试方法。与JUnit 4的@Test注解不同的是，它没有声明任何属性，因为JUnit Jupiter中的测试扩展是基于它们自己的专用注解来完成的。这样的方法会被继承，除非它们被覆盖。</li>
<li><code>@ParameterizedTest</code>: 表示该方法是一个参数化测试（可以用不同的参数多次运行试）。这样的方法会被继承，除非它们被覆盖。</li>
<li><code>@RepeatedTest</code>: 表示该方法是一个重复测试的测试模板(让某个测试方法运行多次)。这样的方法会被继承，除非它们被覆盖。</li>
<li><code>@TestFactory</code>: 表示该方法是一个动态测试的测试工厂。这样的方法会被继承，除非它们被覆盖。</li>
<li><code>@TestInstance</code>: 用于配置所标注的测试类的测试实例生命周期。这些注解会被继承。</li>
<li><code>@TestTemplate</code>: 表示该方法是一个测试模板，它会依据注册的提供者所返回的调用上下文的数量被多次调用。这样的方法会被继承，除非它们被覆盖。</li>
<li><code>@DisplayName</code>: 为测试类或测试方法声明一个自定义的显示名称(空格、特殊字符甚至是emojis表情)。该注解不能被继承。</li>
<li><code>@BeforeEach</code>: 表示使用了该注解的方法应该在当前类中每一个使用了<code>@Test</code>、<code>@RepeatedTest</code>、<code>@ParameterizedTest</code>或者<code>@TestFactory</code>注解的方法之前执行；类似于 JUnit4 的<code>@Before</code>。这样的方法会被继承，除非它们被覆盖。</li>
<li><code>@AfterEach</code>: 表示使用了该注解的方法应该在当前类中每一个使用了<code>@Test</code>、<code>@RepeatedTest</code>、<code>@ParameterizedTest</code>或者<code>@TestFactory</code>注解的方法之后执行；类似于 JUnit4 的<code>@After</code>。这样的方法会被继承，除非它们被覆盖。</li>
<li><code>@BeforeAll</code>: 表示使用了该注解的方法应该在当前类中所有使用了<code>@Test</code>、<code>@RepeatedTest</code>、<code>@ParameterizedTest</code>或者<code>@TestFactory</code>注解的方法之前执行；类似于 JUnit4 的<code>@BeforeClass</code>。这样的方法会被继承（除非它们被隐藏或覆盖），并且它必须是<code>static</code>方法（除非”per-class” 测试实例生命周期被使用）。</li>
<li><code>@AfterAll</code>: 表示使用了该注解的方法应该在当前类中所有使用了<code>@Test</code>、<code>@RepeatedTest</code>、<code>@ParameterizedTest</code>或者<code>@TestFactory</code>注解的方法之后执行；类似于 JUnit4 的<code>@AfterClass</code>。这样的方法会被继承（除非它们被隐藏 或覆盖），并且它必须是<code>static</code>方法（除非”per-class” 测试实例生命周期被使用）。</li>
<li><code>@Nested</code>: 表示使用了该注解的类是一个内嵌、非静态的测试类(让测试编写者能够表示出几组测试用例之间的关系)。<code>@BeforeAll</code>和@<code>AfterAll</code>方法不能直接在<code>@Nested</code>测试类中使用，（除非”per-class”测试实例生命周期被使用）。该注解不能被继承。</li>
<li><code>@Tag</code>: 用于声明过滤测试的tags，该注解可以用在方法或类上；类似于<code>TesgNG</code>的测试组或 JUnit4 的分类。该注解能被继承，但仅限于类级别，而非方法级别。</li>
<li><code>@Disable</code>: 用于禁用一个测试类或测试方法；类似于 JUnit4 的<code>@Ignore</code>。该注解不能被继承。</li>
<li><code>@ExtendWith</code>: 用于注册自定义扩展。该注解不能被继承。</li>
</ul>
<blockquote>
<p><strong>注</strong>：被@Test、@TestTemplate、@RepeatedTest、@BeforeAll、@AfterAll、@BeforeEach 或 @AfterEach 注解标注的方法不可以有返回值。</p>
</blockquote>
<p>在 JUnit5 中的一个测试类的基本生命周期示例如下：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@DisplayName</span><span class="token punctuation">(</span><span class="token string">"Junit5的测试示例类"</span><span class="token punctuation">)</span>
<span class="token keyword">class</span> <span class="token class-name">LifecycleTest</span> <span class="token punctuation">&#123;</span>

    <span class="token annotation punctuation">@BeforeAll</span>
    <span class="token annotation punctuation">@DisplayName</span><span class="token punctuation">(</span><span class="token string">"资源初始化方法"</span><span class="token punctuation">)</span>
    <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">initializeExternalResources</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"Initializing external resources..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token annotation punctuation">@BeforeEach</span>
    <span class="token keyword">void</span> <span class="token function">initializeMockObjects</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"Initializing mock objects..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token annotation punctuation">@Test</span>
    <span class="token keyword">void</span> <span class="token function">someTest</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"Running some test..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">assertTrue</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token annotation punctuation">@Test</span>
    <span class="token keyword">void</span> <span class="token function">otherTest</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token function">assumeTrue</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"Running another test..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">assertNotEquals</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">42</span><span class="token punctuation">,</span> <span class="token string">"Why wouldn't these be the same?"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token annotation punctuation">@Test</span>
    <span class="token annotation punctuation">@Disabled</span>
    <span class="token annotation punctuation">@DisplayName</span><span class="token punctuation">(</span><span class="token string">"该方法先不执行."</span><span class="token punctuation">)</span>
    <span class="token keyword">void</span> <span class="token function">disabledTest</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token class-name">System</span><span class="token punctuation">.</span><span class="token function">exit</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token annotation punctuation">@AfterEach</span>
    <span class="token keyword">void</span> <span class="token function">tearDown</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"Tearing down..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token annotation punctuation">@AfterAll</span>
    <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">freeExternalResources</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"Freeing external resources..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>由于 JUnit5 中的新特性很多，限于篇幅就简单介绍到这里了，如想详细了解 Junit5 的更多特性，请前往<a target="_blank" rel="noopener" href="https://junit.org/junit5/">Junit5官网</a>和<a target="_blank" rel="noopener" href="http://sjyuan.cc/junit5/user-guide-cn/">JUnit5用户指南中文版</a>去查看。</p>
<h3 id="3-Mockito"><a href="#3-Mockito" class="headerlink" title="3. Mockito"></a>3. Mockito</h3><p>在软件开发中提及<strong>Mock</strong>，通常理解为模拟对象。为什么需要模拟? 在我们一开始学编程时,我们所写的对象通常都是独立的，并不依赖其他的类，也不会操作别的类。但实际上，软件中是充满依赖关系的，比如我们会基于 service 业务操作类,而 service 类又是基于数据访问类(DAO)的，依次下去，形成复杂的依赖关系。</p>
<p>单元测试的思路就是我们想在不涉及依赖关系的情况下测试代码。这种测试可以让你无视代码的依赖关系去测试代码的有效性。核心思想就是如果代码按设计正常工作，并且依赖关系也正常，那么他们应该会同时工作正常。</p>
<p>有些时候，我们代码所需要的依赖可能尚未开发完成，甚至还不存在，那如何让我们的开发进行下去呢？使用mock可以让开发进行下去，mock技术的目的和作用就是<strong>模拟一些在应用中不容易构造或者比较复杂的对象，从而把测试与测试边界以外的对象隔离开</strong>。</p>
<p>我们可以自己编写自定义的 Mock 对象实现 Mock 技术，但是编写自定义的 Mock 对象需要额外的编码工作，同时也可能引入错误。现在实现 Mock 技术的优秀开源框架有很多，<a target="_blank" rel="noopener" href="http://site.mockito.org/">Mockito</a>就是一个优秀的用于单元测试的 Mock 框架。</p>
<p>除了Mockito以外，还有一些类似的框架，比如：</p>
<ul>
<li>**<a target="_blank" rel="noopener" href="http://easymock.org/">EasyMock</a>**：早期比较流行的 MocK 测试框架。它提供对接口的模拟，能够通过录制、回放、检查三步来完成大体的测试过程，可以验证方法的调用种类、次数、顺序，可以令 Mock 对象返回指定的值或抛出指定异常。</li>
<li>**<a target="_blank" rel="noopener" href="https://github.com/powermock/powermock">PowerMock</a>**：这个工具是在 EasyMock 和 Mockito 上扩展出来的，目的是为了解决 EasyMock 和 Mockito 不能解决的问题（比如对<code>static</code>, <code>final</code>, <code>private</code>方法均不能 Mock）。其实测试架构设计良好的代码，一般并不需要这些功能，但如果是在已有项目上增加单元测试，老代码有问题且不能改时，就不得不使用这些功能了。</li>
<li>**<a target="_blank" rel="noopener" href="http://jmockit.org/">JMockit</a>**：JMockit 是一个轻量级的mock框架是用以帮助开发人员编写测试程序的一组工具和API，该项目完全基于<code>Java 5 SE</code>的 java.lang.instrument<code>包开发，内部使用</code>ASM<code>库来修改Java的</code>Bytecode`。</li>
<li><strong><a target="_blank" rel="noopener" href="http://wiremock.org/">WireMock</a></strong>: 模拟您的API以进行快速、可靠和全面的测试。<code>WireMock</code>是一个基于 HTTP 的 API 的模拟器。有些人可能认为它是一个服务虚拟化工具或模拟服务器。</li>
</ul>
<p>Mockito 已经被广泛应用，所以这里重点介绍 Mockito，其他的Mock框架也各自有自己的特点，大家下来自己学习或者分享，参考的<a target="_blank" rel="noopener" href="https://github.com/hehonghui/mockito-doc-zh">Mockito中文文档</a>在这里。</p>
<p>下面的例子大多都会模拟一个 List，因为大多数人都熟悉它（比如<code>add()</code>，<code>get()</code>，<code>clear()</code>等方法）。实际上，请不要模拟List类，改用真实的实例。</p>
<h4 id="1-验证行为"><a href="#1-验证行为" class="headerlink" title="(1). 验证行为"></a>(1). 验证行为</h4><p>一旦创建，mock会记录所有交互，你可以验证所有你想要验证的东西。</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// 静态导入会使代码更简洁</span>
<span class="token keyword">import</span> <span class="token keyword">static</span> <span class="token namespace">org<span class="token punctuation">.</span>mockito<span class="token punctuation">.</span></span><span class="token class-name">Mockito</span><span class="token punctuation">.</span>*<span class="token punctuation">;</span>

<span class="token comment">// 创建mock对象</span>
<span class="token class-name">List</span> mockedList <span class="token operator">=</span> <span class="token function">mock</span><span class="token punctuation">(</span><span class="token class-name">List</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 使用mock对象</span>
mockedList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"one"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
mockedList<span class="token punctuation">.</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 验证行为</span>
<span class="token function">verify</span><span class="token punctuation">(</span>mockedList<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"one"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">verify</span><span class="token punctuation">(</span>mockedList<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>Mock一旦创建，模拟对象将记住你的所有的交互。然后，您可以选择性地验证您感兴趣的任何行为。</p>
<h4 id="2-如何做一些测试打桩-stubbing"><a href="#2-如何做一些测试打桩-stubbing" class="headerlink" title="(2). 如何做一些测试打桩(stubbing)"></a>(2). 如何做一些测试打桩(stubbing)</h4><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// 你可以mock具体的类型,不仅只是接口</span>
<span class="token class-name">LinkedList</span> mockedList <span class="token operator">=</span> <span class="token function">mock</span><span class="token punctuation">(</span><span class="token class-name">LinkedList</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 测试桩</span>
<span class="token function">when</span><span class="token punctuation">(</span>mockedList<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">thenReturn</span><span class="token punctuation">(</span><span class="token string">"first"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">when</span><span class="token punctuation">(</span>mockedList<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">thenThrow</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">RuntimeException</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 输出“first”</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>mockedList<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 抛出异常</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>mockedList<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 因为get(999) 没有打桩，因此输出null</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>mockedList<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token number">999</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 验证get(0)被调用的次数</span>
<span class="token function">verify</span><span class="token punctuation">(</span>mockedList<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<ul>
<li>默认情况下，所有的函数都有返回值。mock函数默认返回的是null，一个空的集合或者一个被对象类型包装的内置类型，例如<code>0</code>、<code>false</code>对应的对象类型为<code>Integer</code>、<code>Boolean</code>；</li>
<li>测试桩函数可以被覆写: 例如常见的测试桩函数可以用于初始化夹具，但是测试函数能够覆写它。请注意，覆写测试桩函数是一种可能存在潜在问题的做法；</li>
<li>一旦测试桩函数被调用，该函数将会一致返回固定的值；</li>
<li>上一次调用测试桩函数有时候极为重要，当你调用一个函数很多次时，最后一次调用可能是你所感兴趣的。</li>
</ul>
<h4 id="3-参数匹配器-matchers"><a href="#3-参数匹配器-matchers" class="headerlink" title="(3). 参数匹配器(matchers)"></a>(3). 参数匹配器(matchers)</h4><p>Mockito以自然的java风格来验证参数值: 使用<code>equals()</code>函数。有时，当需要额外的灵活性时你可能需要使用参数匹配器，也就是<code>argument matchers</code>:</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// 使用内置的anyInt()参数匹配器</span>
<span class="token function">when</span><span class="token punctuation">(</span>mockedList<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token function">anyInt</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">thenReturn</span><span class="token punctuation">(</span><span class="token string">"element"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 使用自定义的参数匹配器( 在isValid()函数中返回你自己的匹配器实现 )</span>
<span class="token function">when</span><span class="token punctuation">(</span>mockedList<span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span><span class="token function">argThat</span><span class="token punctuation">(</span><span class="token function">isValid</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">thenReturn</span><span class="token punctuation">(</span><span class="token string">"element"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 输出element</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>mockedList<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token number">999</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 你也可以验证参数匹配器</span>
<span class="token function">verify</span><span class="token punctuation">(</span>mockedList<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token function">anyInt</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>参数匹配器使验证和测试桩变得更灵活。点击<a target="_blank" rel="noopener" href="https://static.javadoc.io/org.mockito/mockito-core/2.18.3/org/mockito/Matchers.html">这里</a>可以查看更多内置的匹配器以及自定义参数匹配器或者hamcrest 匹配器的示例。</p>
<h4 id="4-验证函数的确切、最少、从未调用次数"><a href="#4-验证函数的确切、最少、从未调用次数" class="headerlink" title="(4). 验证函数的确切、最少、从未调用次数"></a>(4). 验证函数的确切、最少、从未调用次数</h4><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// 使用模拟对象</span>
mockedList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"once"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

mockedList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"twice"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
mockedList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"twice"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

mockedList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"three times"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
mockedList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"three times"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
mockedList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"three times"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 下面的两个验证函数效果一样,因为verify默认验证的就是times(1)</span>
<span class="token function">verify</span><span class="token punctuation">(</span>mockedList<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"once"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">verify</span><span class="token punctuation">(</span>mockedList<span class="token punctuation">,</span> <span class="token function">times</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"once"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 验证具体的执行次数</span>
<span class="token function">verify</span><span class="token punctuation">(</span>mockedList<span class="token punctuation">,</span> <span class="token function">times</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"twice"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">verify</span><span class="token punctuation">(</span>mockedList<span class="token punctuation">,</span> <span class="token function">times</span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"three times"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 使用never()进行验证,never相当于times(0)</span>
<span class="token function">verify</span><span class="token punctuation">(</span>mockedList<span class="token punctuation">,</span> <span class="token function">never</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"never happened"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 使用atLeast()/atMost()</span>
<span class="token function">verify</span><span class="token punctuation">(</span>mockedList<span class="token punctuation">,</span> <span class="token function">atLeastOnce</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"three times"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">verify</span><span class="token punctuation">(</span>mockedList<span class="token punctuation">,</span> <span class="token function">atLeast</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"five times"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">verify</span><span class="token punctuation">(</span>mockedList<span class="token punctuation">,</span> <span class="token function">atMost</span><span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"three times"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p><code>verify</code>函数默认验证的是执行了<code>times(1)</code>，也就是某个测试函数是否执行了1次.因此，<code>times(1)</code>通常被省略了。</p>
<h4 id="5-为返回值为void的函数通过Stub抛出异常"><a href="#5-为返回值为void的函数通过Stub抛出异常" class="headerlink" title="(5). 为返回值为void的函数通过Stub抛出异常"></a>(5). 为返回值为void的函数通过Stub抛出异常</h4><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token function">doThrow</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">RuntimeException</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">when</span><span class="token punctuation">(</span>mockedList<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 调用这句代码会抛出异常</span>
mockedList<span class="token punctuation">.</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre>

<p>当你调用<code>doThrow()</code>, <code>doAnswer()</code>, <code>doNothing()</code>, <code>doReturn()</code> and <code>doCallRealMethod()</code> 这些函数时可以在适当的位置调用<code>when()</code>函数. 当你需要下面这些功能时这是必须的:</p>
<ul>
<li>测试void函数</li>
<li>在受监控的对象上测试函数</li>
<li>不知一次的测试为同一个函数，在测试过程中改变mock对象的行为。</li>
</ul>
<p>但是在调用<code>when()</code>函数时你可以选择是否调用这些上述这些函数。</p>
<h4 id="6-验证执行执行顺序"><a href="#6-验证执行执行顺序" class="headerlink" title="(6). 验证执行执行顺序"></a>(6). 验证执行执行顺序</h4><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// A. 验证mock一个对象的函数执行顺序</span>
<span class="token comment">// 创建Mock对象</span>
<span class="token class-name">List</span> singleMock <span class="token operator">=</span> <span class="token function">mock</span><span class="token punctuation">(</span><span class="token class-name">List</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 使用mock对象</span>
singleMock<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"was added first"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
singleMock<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"was added second"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 为该mock对象创建一个inOrder对象</span>
<span class="token class-name">InOrder</span> inOrder <span class="token operator">=</span> <span class="token function">inOrder</span><span class="token punctuation">(</span>singleMock<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 确保add函数首先执行的是add("was added first"),然后才是add("was added second")</span>
inOrder<span class="token punctuation">.</span><span class="token function">verify</span><span class="token punctuation">(</span>singleMock<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"was added first"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
inOrder<span class="token punctuation">.</span><span class="token function">verify</span><span class="token punctuation">(</span>singleMock<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"was added second"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// B .验证多个mock对象的函数执行顺序</span>
<span class="token class-name">List</span> firstMock <span class="token operator">=</span> <span class="token function">mock</span><span class="token punctuation">(</span><span class="token class-name">List</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">List</span> secondMock <span class="token operator">=</span> <span class="token function">mock</span><span class="token punctuation">(</span><span class="token class-name">List</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 使用mock对象</span>
firstMock<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"was called first"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
secondMock<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"was called second"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 为这两个Mock对象创建inOrder对象</span>
<span class="token class-name">InOrder</span> inOrder <span class="token operator">=</span> <span class="token function">inOrder</span><span class="token punctuation">(</span>firstMock<span class="token punctuation">,</span> secondMock<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 验证它们的执行顺序</span>
inOrder<span class="token punctuation">.</span><span class="token function">verify</span><span class="token punctuation">(</span>firstMock<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"was called first"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
inOrder<span class="token punctuation">.</span><span class="token function">verify</span><span class="token punctuation">(</span>secondMock<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"was called second"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>验证执行顺序是非常灵活的。你不需要一个一个的验证所有交互,只需要验证你感兴趣的对象即可。另外，你可以仅通过那些需要验证顺序的mock对象来创建<code>InOrder</code>对象。</p>
<h4 id="7-确保交互-interaction-操作不会执行在mock对象上"><a href="#7-确保交互-interaction-操作不会执行在mock对象上" class="headerlink" title="(7). 确保交互(interaction)操作不会执行在mock对象上"></a>(7). 确保交互(interaction)操作不会执行在mock对象上</h4><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// 使用Mock对象</span>
mockOne<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"one"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 普通验证</span>
<span class="token function">verify</span><span class="token punctuation">(</span>mockOne<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"one"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 验证某个交互是否从未被执行</span>
<span class="token function">verify</span><span class="token punctuation">(</span>mockOne<span class="token punctuation">,</span> <span class="token function">never</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"two"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 验证mock对象没有交互过</span>
<span class="token function">verifyZeroInteractions</span><span class="token punctuation">(</span>mockTwo<span class="token punctuation">,</span> mockThree<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<h4 id="8-查找冗余的调用"><a href="#8-查找冗余的调用" class="headerlink" title="(8). 查找冗余的调用"></a>(8). 查找冗余的调用</h4><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// 使用mock对象</span>
mockedList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"one"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
mockedList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"two"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token function">verify</span><span class="token punctuation">(</span>mockedList<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"one"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 下面的验证将会失败</span>
<span class="token function">verifyNoMoreInteractions</span><span class="token punctuation">(</span>mockedList<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>一些用户可能会在频繁地使用<code>verifyNoMoreInteractions()</code>，甚至在每个测试函数中都用。但是<code>verifyNoMoreInteractions()</code>并不建议在每个测试函数中都使用。<code>verifyNoMoreInteractions()</code>在交互测试套件中只是一个便利的验证，它的作用是当你需要验证是否存在冗余调用时。滥用它将导致测试代码的可维护性降低。你可以阅读这篇文档来了解更多相关信息。</p>
<h4 id="9-简化mock对象的创建"><a href="#9-简化mock对象的创建" class="headerlink" title="(9). 简化mock对象的创建"></a>(9). 简化mock对象的创建</h4><ul>
<li>最小化重复的创建代码;</li>
<li>使测试类的代码可读性更高;</li>
<li>使验证错误更易于阅读，因为字段名可用于标识mock对象;</li>
</ul>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ArticleManagerTest</span> <span class="token punctuation">&#123;</span>

   <span class="token annotation punctuation">@Mock</span> <span class="token keyword">private</span> <span class="token class-name">ArticleCalculator</span> calculator<span class="token punctuation">;</span>
   <span class="token annotation punctuation">@Mock</span> <span class="token keyword">private</span> <span class="token class-name">ArticleDatabase</span> database<span class="token punctuation">;</span>
   <span class="token annotation punctuation">@Mock</span> <span class="token keyword">private</span> <span class="token class-name">UserProvider</span> userProvider<span class="token punctuation">;</span>

   <span class="token keyword">private</span> <span class="token class-name">ArticleManager</span> manager<span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>注意！下面这句代码需要在运行测试函数之前被调用,一般放到测试类的基类或者test runner中:</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">MockitoAnnotations</span><span class="token punctuation">.</span><span class="token function">initMocks</span><span class="token punctuation">(</span>testClass<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre>

<p>关于mock注解的更多信息可以阅读<a target="_blank" rel="noopener" href="https://static.javadoc.io/org.mockito/mockito-core/2.18.3/org/mockito/MockitoAnnotations.html">MockitoAnnotations文档</a>。</p>
<h4 id="10-为连续的调用做测试打桩-stub"><a href="#10-为连续的调用做测试打桩-stub" class="headerlink" title="(10). 为连续的调用做测试打桩 (stub)"></a>(10). 为连续的调用做测试打桩 (stub)</h4><p>有时我们需要为同一个函数调用的不同的返回值或异常做测试桩。</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token function">when</span><span class="token punctuation">(</span>mock<span class="token punctuation">.</span><span class="token function">someMethod</span><span class="token punctuation">(</span><span class="token string">"some arg"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">thenThrow</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">RuntimeException</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">thenReturn</span><span class="token punctuation">(</span><span class="token string">"foo"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 第一次调用 : 抛出运行时异常</span>
mock<span class="token punctuation">.</span><span class="token function">someMethod</span><span class="token punctuation">(</span><span class="token string">"some arg"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 第二次调用 : 输出"foo"</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>mock<span class="token punctuation">.</span><span class="token function">someMethod</span><span class="token punctuation">(</span><span class="token string">"some arg"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 后续调用 : 也是输出"foo"</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>mock<span class="token punctuation">.</span><span class="token function">someMethod</span><span class="token punctuation">(</span><span class="token string">"some arg"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>另外，连续调用的另一种更简短的版本 :</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// 第一次调用时返回"one",第二次返回"two",第三次返回"three"</span>
<span class="token function">when</span><span class="token punctuation">(</span>mock<span class="token punctuation">.</span><span class="token function">someMethod</span><span class="token punctuation">(</span><span class="token string">"some arg"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">thenReturn</span><span class="token punctuation">(</span><span class="token string">"one"</span><span class="token punctuation">,</span> <span class="token string">"two"</span><span class="token punctuation">,</span> <span class="token string">"three"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre>

<h4 id="11-为回调做测试桩"><a href="#11-为回调做测试桩" class="headerlink" title="(11). 为回调做测试桩"></a>(11). 为回调做测试桩</h4><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token function">when</span><span class="token punctuation">(</span>mock<span class="token punctuation">.</span><span class="token function">someMethod</span><span class="token punctuation">(</span><span class="token function">anyString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">thenAnswer</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Answer</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
     <span class="token class-name">Object</span> <span class="token function">answer</span><span class="token punctuation">(</span><span class="token class-name">InvocationOnMock</span> invocation<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
         <span class="token class-name">Object</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args <span class="token operator">=</span> invocation<span class="token punctuation">.</span><span class="token function">getArguments</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
         <span class="token class-name">Object</span> mock <span class="token operator">=</span> invocation<span class="token punctuation">.</span><span class="token function">getMock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
         <span class="token keyword">return</span> <span class="token string">"called with arguments: "</span> <span class="token operator">+</span> args<span class="token punctuation">;</span>
     <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 输出 : "called with arguments: foo"</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>mock<span class="token punctuation">.</span><span class="token function">someMethod</span><span class="token punctuation">(</span><span class="token string">"foo"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<h4 id="12-监控真实对象"><a href="#12-监控真实对象" class="headerlink" title="(12). 监控真实对象"></a>(12). 监控真实对象</h4><p>你可以为真实对象创建一个监控(spy)对象。当你使用这个<code>spy</code>对象时真实的对象也会也调用，除非它的函数被stub了。<strong>尽量少使用spy对象</strong>，使用时也需要小心形式，例如spy对象可以用来处理遗留代码。</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">List</span> list <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">LinkedList</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">List</span> spy <span class="token operator">=</span> <span class="token function">spy</span><span class="token punctuation">(</span>list<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 你可以为某些函数打桩</span>
<span class="token function">when</span><span class="token punctuation">(</span>spy<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">thenReturn</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 通过spy对象调用真实对象的函数</span>
spy<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"one"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
spy<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"two"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 输出第一个元素</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>spy<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 因为size()函数被打桩了,因此这里返回的是100</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>spy<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// 交互验证</span>
<span class="token function">verify</span><span class="token punctuation">(</span>spy<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"one"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">verify</span><span class="token punctuation">(</span>spy<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"two"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>Mockito 并不会为真实对象代理函数调用，实际上它会拷贝真实对象。因此如果你保留了真实对象并且与之交互，不要期望从监控对象得到正确的结果。当你在监控对象上调用一个没有被stub的函数时并不会调用真实对象的对应函数，你不会在真实对象上看到任何效果。</p>
<p>因此结论就是: 当你在监控一个真实对象时，你想在stub这个真实对象的函数，那么就是在自找麻烦。或者你根本不应该验证这些函数。</p>
<h4 id="13-重置mocks对象"><a href="#13-重置mocks对象" class="headerlink" title="(13). 重置mocks对象"></a>(13). 重置mocks对象</h4><p>聪明的 Mockito 使用者很少会用到这个特性，因为他们知道这是出现糟糕测试单元的信号。通常情况下你不会需要重设你的测试单元，只需要为每一个测试方法重新创建一个测试单元就可以了。</p>
<p>如果你真的想通过<code>reset()</code>方法满足某些需求的话，请考虑实现简单，小而且专注于测试方法而不是冗长，精确的测试。首先可能出现的代码异味就是测试方法中间那的<code>reset()</code>方法。这可能意味着你已经过度测试了。</p>
<p>添加 reset() 方法的唯一原因就是让它能与容器注入的测试单元协作。</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">List</span> mock <span class="token operator">=</span> <span class="token function">mock</span><span class="token punctuation">(</span><span class="token class-name">List</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">when</span><span class="token punctuation">(</span>mock<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">thenReturn</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
mock<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token function">reset</span><span class="token punctuation">(</span>mock<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">//at this point the mock forgot any interactions &amp; stubbing</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<h4 id="14-更多的注解"><a href="#14-更多的注解" class="headerlink" title="(14). 更多的注解"></a>(14). 更多的注解</h4><ul>
<li><code>@Captor</code>: 创建<code>ArgumentCaptor</code>。</li>
<li><code>@Spy</code>: 可以代替<code>spy(Object)</code>。</li>
<li><code>@InjectMocks</code>: 如果此注解声明的变量需要用到mock对象，mockito会自动注入mock或spy成员。</li>
</ul>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">//可以这样写</span>
<span class="token annotation punctuation">@Spy</span>
<span class="token class-name">BeerDrinker</span> drinker <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">BeerDrinker</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">//也可以这样写，mockito会自动实例化drinker.</span>
<span class="token annotation punctuation">@Spy</span>
<span class="token class-name">BeerDrinker</span> drinker<span class="token punctuation">;</span>

<span class="token comment">//会自动实例化LocalPub</span>
<span class="token annotation punctuation">@InjectMocks</span>
<span class="token class-name">LocalPub</span> pub<span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<h4 id="15-BDD-风格的验证-Since-1-10-0"><a href="#15-BDD-风格的验证-Since-1-10-0" class="headerlink" title="(15). BDD 风格的验证(Since 1.10.0)"></a>(15). BDD 风格的验证(Since 1.10.0)</h4><p>开启<code>Behavior Driven Development</code>(BDD，即行为驱动开发)风格的验证可以通过<code>BBD</code>的关键词<code>then</code>开始验证。</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token function">given</span><span class="token punctuation">(</span>dog<span class="token punctuation">.</span><span class="token function">bark</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">willReturn</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// when</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

<span class="token function">then</span><span class="token punctuation">(</span>person<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">should</span><span class="token punctuation">(</span><span class="token function">times</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">ride</span><span class="token punctuation">(</span>bike<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>以上就是 Mockito 的主要使用方式，关于更详细的介绍可参考<a target="_blank" rel="noopener" href="https://static.javadoc.io/org.mockito/mockito-core/2.18.3/org/mockito/Mockito.html">Mockito官方文档</a>和<a target="_blank" rel="noopener" href="https://github.com/hehonghui/mockito-doc-zh">Mockito中文文档</a>。</p>
<h3 id="4-Spring-Test"><a href="#4-Spring-Test" class="headerlink" title="4. Spring Test"></a>4. Spring Test</h3><p>目前几乎大多数 Java web 项目都是有基于 Spring 来开发的。通过 Spring 进行 bean 管理后，仅仅通过 JUnit 来做测试会有各种麻烦，比如：Spring容器初始化问题、使用硬编码方式手工获取Bean、不方便对数据操作的正确性做检查等。这时我们就可以通过 Spring 全家桶中的另一位成员<strong>spring-test</strong>来帮助我们在 Spring 工程中做单元测试了。以下通过简单的示例来演示其使用。</p>
<h4 id="1-加入依赖包"><a href="#1-加入依赖包" class="headerlink" title="(1). 加入依赖包"></a>(1). 加入依赖包</h4><p>通过Maven加入<code>JUnit</code>、<code>spring-test</code>的Jar包(最好其他Spring包版本一致)。</p>
<pre class="line-numbers language-xml" data-language="xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dependency</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>groupId</span><span class="token punctuation">></span></span>junit<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>groupId</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">></span></span>junit<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>version</span><span class="token punctuation">></span></span>4.12<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>version</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>scope</span><span class="token punctuation">></span></span>test<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>scope</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dependency</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dependency</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>groupId</span><span class="token punctuation">></span></span>org.springframework<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>groupId</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">></span></span>spring-test<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>version</span><span class="token punctuation">></span></span>xxxx<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>version</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>scope</span><span class="token punctuation">></span></span>test<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>scope</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dependency</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<h4 id="2-创建测试类"><a href="#2-创建测试类" class="headerlink" title="(2). 创建测试类"></a>(2). 创建测试类</h4><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@RunWith</span><span class="token punctuation">(</span><span class="token class-name">SpringJUnit4ClassRunner</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span>
<span class="token annotation punctuation">@ContextConfiguration</span><span class="token punctuation">(</span><span class="token string">"/application-context-test.xml"</span><span class="token punctuation">)</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">UserDaoTest</span> <span class="token punctuation">&#123;</span>

    <span class="token comment">/** 自动注入baseDao,默认按名称. */</span>
    <span class="token annotation punctuation">@Resource</span>
    <span class="token keyword">private</span> <span class="token class-name">IBaseDao</span> baseDao<span class="token punctuation">;</span>

    <span class="token annotation punctuation">@Test</span>
    <span class="token annotation punctuation">@Transactional</span>
    <span class="token annotation punctuation">@Rollback</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">insert</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token class-name">String</span> sql <span class="token operator">=</span> <span class="token string">"INSERT INTO t_user(c_name, c_password) values(?, ?)"</span><span class="token punctuation">;</span>
        <span class="token class-name">Object</span><span class="token punctuation">[</span><span class="token punctuation">]</span> objs <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Object</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">&#123;</span><span class="token string">"zhangsan"</span><span class="token punctuation">,</span> <span class="token string">"123456"</span><span class="token punctuation">&#125;</span><span class="token punctuation">;</span>
        baseDao<span class="token punctuation">.</span><span class="token function">insert</span><span class="token punctuation">(</span>sql <span class="token punctuation">,</span> objs<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token class-name">String</span> sql2 <span class="token operator">=</span> <span class="token string">"SELECT * FROM t_user WHERE c_name = ? and c_password = ?"</span><span class="token punctuation">;</span>
        <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Map</span><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">,</span><span class="token class-name">Object</span><span class="token punctuation">></span><span class="token punctuation">></span></span> list <span class="token operator">=</span> baseDao<span class="token punctuation">.</span><span class="token function">queryForList</span><span class="token punctuation">(</span>sql1<span class="token punctuation">,</span> objs<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">assertTrue</span><span class="token punctuation">(</span>list<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>list<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<ul>
<li>使用Spring Test 可以使用<code>@Autowired</code>自动注入相关的bean信息，而不需要自己手动通过<code>getBean</code>去获取相应的bean信息。</li>
<li>使用Spring Test 测试，可以<code>@Transaction</code>注解，表示该方法使用spring的事务，在单元测试中，执行完毕后默认会回滚。</li>
<li>使用<code>@Rollback</code>注解，标明使用完此方法后事务回滚，可以<code>@Rollback(false)</code>这个注解来使对数据库操作的测试结果不回滚。</li>
</ul>
<h4 id="3-对-Spring-MVC-的测试"><a href="#3-对-Spring-MVC-的测试" class="headerlink" title="(3). 对 Spring MVC 的测试"></a>(3). 对 Spring MVC 的测试</h4><p>为了测试 web 项目，需要一些 Servlet 相关的模拟对象，比如：<code>MockMVC</code>/<code>MockHttpServletRequest</code>/<code>MockHttpServletResponse</code>/<code>MockHttpSession</code>。使用示例如下：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">import</span> <span class="token keyword">static</span> <span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>test<span class="token punctuation">.</span>web<span class="token punctuation">.</span>servlet<span class="token punctuation">.</span>request<span class="token punctuation">.</span></span><span class="token class-name">MockMvcRequestBuilders</span><span class="token punctuation">.</span>get<span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token keyword">static</span> <span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>test<span class="token punctuation">.</span>web<span class="token punctuation">.</span>servlet<span class="token punctuation">.</span>result<span class="token punctuation">.</span></span><span class="token class-name">MockMvcResultMatchers</span><span class="token punctuation">.</span>content<span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token keyword">static</span> <span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>test<span class="token punctuation">.</span>web<span class="token punctuation">.</span>servlet<span class="token punctuation">.</span>result<span class="token punctuation">.</span></span><span class="token class-name">MockMvcResultMatchers</span><span class="token punctuation">.</span>forwardedUrl<span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token keyword">static</span> <span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>test<span class="token punctuation">.</span>web<span class="token punctuation">.</span>servlet<span class="token punctuation">.</span>result<span class="token punctuation">.</span></span><span class="token class-name">MockMvcResultMatchers</span><span class="token punctuation">.</span>model<span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token keyword">static</span> <span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>test<span class="token punctuation">.</span>web<span class="token punctuation">.</span>servlet<span class="token punctuation">.</span>result<span class="token punctuation">.</span></span><span class="token class-name">MockMvcResultMatchers</span><span class="token punctuation">.</span>status<span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token keyword">static</span> <span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>test<span class="token punctuation">.</span>web<span class="token punctuation">.</span>servlet<span class="token punctuation">.</span>result<span class="token punctuation">.</span></span><span class="token class-name">MockMvcResultMatchers</span><span class="token punctuation">.</span>view<span class="token punctuation">;</span>

<span class="token keyword">import</span> <span class="token namespace">org<span class="token punctuation">.</span>junit<span class="token punctuation">.</span></span><span class="token class-name">Before</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token namespace">org<span class="token punctuation">.</span>junit<span class="token punctuation">.</span></span><span class="token class-name">Test</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token namespace">org<span class="token punctuation">.</span>junit<span class="token punctuation">.</span>runner<span class="token punctuation">.</span></span><span class="token class-name">RunWith</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>beans<span class="token punctuation">.</span>factory<span class="token punctuation">.</span>annotation<span class="token punctuation">.</span></span><span class="token class-name">Autowired</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>mock<span class="token punctuation">.</span>web<span class="token punctuation">.</span></span><span class="token class-name">MockHttpServletRequest</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>mock<span class="token punctuation">.</span>web<span class="token punctuation">.</span></span><span class="token class-name">MockHttpSession</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>test<span class="token punctuation">.</span>context<span class="token punctuation">.</span></span><span class="token class-name">ContextConfiguration</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>test<span class="token punctuation">.</span>context<span class="token punctuation">.</span>junit4<span class="token punctuation">.</span></span><span class="token class-name">SpringJUnit4ClassRunner</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>test<span class="token punctuation">.</span>context<span class="token punctuation">.</span>web<span class="token punctuation">.</span></span><span class="token class-name">WebAppConfiguration</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>test<span class="token punctuation">.</span>web<span class="token punctuation">.</span>servlet<span class="token punctuation">.</span></span><span class="token class-name">MockMvc</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>test<span class="token punctuation">.</span>web<span class="token punctuation">.</span>servlet<span class="token punctuation">.</span>setup<span class="token punctuation">.</span></span><span class="token class-name">MockMvcBuilders</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token namespace">org<span class="token punctuation">.</span>springframework<span class="token punctuation">.</span>web<span class="token punctuation">.</span>context<span class="token punctuation">.</span></span><span class="token class-name">WebApplicationContext</span><span class="token punctuation">;</span>

<span class="token annotation punctuation">@RunWith</span><span class="token punctuation">(</span><span class="token class-name">SpringJUnit4ClassRunner</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span>
<span class="token annotation punctuation">@ContextConfiguration</span><span class="token punctuation">(</span><span class="token string">"/application-context-test.xml"</span><span class="token punctuation">)</span>
<span class="token annotation punctuation">@WebAppConfiguration</span><span class="token punctuation">(</span><span class="token string">"src/main/resources"</span><span class="token punctuation">)</span> <span class="token comment">// 此注解指定web资源的位置，默认为src/main/webapp</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">TestControllerIntegrationTests</span> <span class="token punctuation">&#123;</span>

    <span class="token keyword">private</span> <span class="token class-name">MockMvc</span> mockMvc<span class="token punctuation">;</span> <span class="token comment">// 模拟MVC对象</span>

    <span class="token annotation punctuation">@Autowired</span>
    <span class="token keyword">private</span> <span class="token class-name">DemoService</span> demoService<span class="token punctuation">;</span><span class="token comment">// 在测试用例注入spring的bean</span>

    <span class="token annotation punctuation">@Autowired</span>
    <span class="token class-name">WebApplicationContext</span> wac<span class="token punctuation">;</span> <span class="token comment">// 注入WebApplicationContext</span>

    <span class="token annotation punctuation">@Autowired</span>
    <span class="token class-name">MockHttpSession</span> session<span class="token punctuation">;</span> <span class="token comment">// 注入模拟的http session</span>

    <span class="token annotation punctuation">@Autowired</span>
    <span class="token class-name">MockHttpServletRequest</span> request<span class="token punctuation">;</span> <span class="token comment">// 模拟request</span>

    <span class="token annotation punctuation">@Before</span> <span class="token comment">// 测试开始前的初始化工作</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">setup</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>mockMvc <span class="token operator">=</span> <span class="token class-name">MockMvcBuilders</span><span class="token punctuation">.</span><span class="token function">webAppContextSetup</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>wac<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//2</span>
    <span class="token punctuation">&#125;</span>

    <span class="token annotation punctuation">@Test</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">testNormalController</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">Exception</span><span class="token punctuation">&#123;</span>
        <span class="token class-name">String</span> exp_str <span class="token operator">=</span> demoService<span class="token punctuation">.</span><span class="token function">saySomething</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// expect str</span>
        mockMvc<span class="token punctuation">.</span><span class="token function">perform</span><span class="token punctuation">(</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">"/normal"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// 模拟GET /normal</span>
            <span class="token punctuation">.</span><span class="token function">andExpect</span><span class="token punctuation">(</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">isOk</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token comment">// 预期返回状态为200</span>
            <span class="token punctuation">.</span><span class="token function">andExpect</span><span class="token punctuation">(</span><span class="token function">view</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">name</span><span class="token punctuation">(</span><span class="token string">"page"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token comment">// 预期view的名称</span>
            <span class="token punctuation">.</span><span class="token function">andExpect</span><span class="token punctuation">(</span><span class="token function">forwardedUrl</span><span class="token punctuation">(</span><span class="token string">"/WEB-INF/classes/views/page.jsp"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token comment">// 预期页面转向的真正路径</span>
            <span class="token punctuation">.</span><span class="token function">andExpect</span><span class="token punctuation">(</span><span class="token function">model</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">attribute</span><span class="token punctuation">(</span><span class="token string">"msg"</span><span class="token punctuation">,</span> exp_str<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// 预期model里的值</span>
    <span class="token punctuation">&#125;</span>

    <span class="token annotation punctuation">@Test</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">testRestController</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">Exception</span><span class="token punctuation">&#123;</span>
        mockMvc<span class="token punctuation">.</span><span class="token function">perform</span><span class="token punctuation">(</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">"/testRest"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// HTTP GET 方法</span>
            <span class="token punctuation">.</span><span class="token function">andExpect</span><span class="token punctuation">(</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">isOk</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">andExpect</span><span class="token punctuation">(</span><span class="token function">content</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">contentType</span><span class="token punctuation">(</span><span class="token string">"text/plain;charset=UTF-8"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token comment">//14</span>
            <span class="token punctuation">.</span><span class="token function">andExpect</span><span class="token punctuation">(</span><span class="token function">content</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">string</span><span class="token punctuation">(</span>demoService<span class="token punctuation">.</span><span class="token function">saySomething</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">//15</span>
    <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<blockquote>
<p><strong>注</strong>: <code>demoService</code>及相关方法的调用，也可以通过<code>Mockito</code>工具Mock出来，更符合单元测试对<strong>单元性</strong>的要求，否则这些测试又额外附带了一定集成测试的性质了。</p>
</blockquote>
<h3 id="4-spring-boot-starter-test"><a href="#4-spring-boot-starter-test" class="headerlink" title="4. spring-boot-starter-test"></a>4. spring-boot-starter-test</h3><h4 id="1-简单介绍"><a href="#1-简单介绍" class="headerlink" title="(1). 简单介绍"></a>(1). 简单介绍</h4><p>现在越来越多的应用都采用SpringBoot的方式来构建，在SpringBoot应用中单元测试变得更加容易了，只需要加入<code>spring-boot-starter-test</code>的 Starter 即可，其中默认导入了 Spring Boot 测试模块以及<code>JUnit</code>，<code>AssertJ</code>，<code>Hamcrest</code>和其他一些有用的库。</p>
<pre class="line-numbers language-xml" data-language="xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dependency</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>groupId</span><span class="token punctuation">></span></span>org.springframework.boot<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>groupId</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">></span></span>spring-boot-starter-test<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>scope</span><span class="token punctuation">></span></span>test<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>scope</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dependency</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p><code>spring-boot-starter-test</code>的 Starter (Scope为test)，包括了以下提供的类库：</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://junit.org/">JUnit</a>：单元测试Java应用程序的事实标准。</li>
<li><a target="_blank" rel="noopener" href="https://docs.spring.io/spring/docs/5.0.6.RELEASE/spring-framework-reference/testing.html#integration-testing">Spring Test</a> 和 Spring Boot Test：Spring Boot应用程序的实用程序和集成测试支持。</li>
<li><a target="_blank" rel="noopener" href="https://joel-costigliola.github.io/assertj/">AssertJ</a>：流畅的断言库。</li>
<li><a target="_blank" rel="noopener" href="http://hamcrest.org/JavaHamcrest/">Hamcrest</a>：匹配器对象库。</li>
<li><a target="_blank" rel="noopener" href="http://mockito.org/">Mockito</a>：Java Mock 框架。</li>
<li><a target="_blank" rel="noopener" href="https://github.com/skyscreamer/JSONassert">JSONassert</a>：JSON的断言库。</li>
<li><a href="JsonPath">JsonPath</a>：JSON的XPath。</li>
</ul>
<p>我们通常在编写测试时发现这些通用库都是比较有用的。如果这些库还不适合您的需求，您还可以添加您自己的附加测试依赖库。</p>
<p>Spring Boot 提供了一个<code>@SpringBootTest</code>注释，当您需要 Spring Boot 功能时，它可以用作标准 spring-test <code>@ContextConfiguration</code>注释的替代方法。注解的工作原理是通过<code>SpringApplication</code>创建用于测试的<code>ApplicationContext</code>。除了<code>@SpringBootTest</code>之外，还提供了许多其他注释来测试应用程序的更具体的切片。</p>
<blockquote>
<p><strong>提示</strong>：不要忘记在测试中添加<code>@RunWith(SpringRunner.class)</code>，否则注释将被忽略。</p>
</blockquote>
<h4 id="2-一个简单示例"><a href="#2-一个简单示例" class="headerlink" title="(2). 一个简单示例"></a>(2). 一个简单示例</h4><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@RunWith</span><span class="token punctuation">(</span><span class="token class-name">SpringRunner</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span>
<span class="token annotation punctuation">@SpringBootTest</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">UserServiceTest</span> <span class="token punctuation">&#123;</span>

    <span class="token annotation punctuation">@Value</span><span class="token punctuation">(</span><span class="token string">"$&#123;msg&#125;"</span><span class="token punctuation">)</span>
    <span class="token keyword">private</span> <span class="token class-name">String</span> msg<span class="token punctuation">;</span>

    <span class="token annotation punctuation">@Autowired</span>
    <span class="token keyword">private</span> <span class="token class-name">UserService</span> userService<span class="token punctuation">;</span>

    <span class="token annotation punctuation">@Test</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">getUser</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token class-name">User</span> user <span class="token operator">=</span> userService<span class="token punctuation">.</span><span class="token function">selectByKey</span><span class="token punctuation">(</span><span class="token number">20180302325L</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">Assert</span><span class="token punctuation">.</span><span class="token function">assertThat</span><span class="token punctuation">(</span>user<span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">is</span><span class="token punctuation">(</span><span class="token string">"Blinkfox"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"获取的配置信息为:"</span> <span class="token operator">+</span> msg<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>上面就是最简单的单元测试写法，测试类上只需要<code>@RunWith(SpringRunner.class)</code>和<code>@SpringBootTest</code>两个注解即可测试任何类和方法。</p>
<h4 id="3-web模块的单元测试"><a href="#3-web模块的单元测试" class="headerlink" title="(3). web模块的单元测试"></a>(3). web模块的单元测试</h4><p>要测试 Spring MVC 控制器是否按预期工作，请使用<code>@WebMvcTest</code>注释。<code>@WebMvcTest</code>自动配置<code>Spring MVC</code>基础结构，并将扫描的bean限制为<code>@Controller</code>，<code>@ControllerAdvice</code>，<code>@JsonComponent</code>，<code>Converter</code>，<code>GenericConverter</code>，<code>Filter</code>，<code>WebMvcConfigurer</code>和<code>HandlerMethodArgumentResolver</code>。 使用此注释时，不会扫描常规的<code>@Component</code> bean。</p>
<p>您还可以使用<code>@AutoConfigureMockMvc</code>对其进行注释，从而在非<code>@WebMvcTest</code>（如<code>@SpringBootTest</code>）中自动配置<code>MockMvc</code>。 以下示例使用MockMvc：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@RunWith</span><span class="token punctuation">(</span><span class="token class-name">SpringRunner</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span>
<span class="token annotation punctuation">@WebMvcTest</span><span class="token punctuation">(</span><span class="token class-name">UserVehicleController</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">MyControllerTests</span> <span class="token punctuation">&#123;</span>

    <span class="token annotation punctuation">@Autowired</span>
    <span class="token keyword">private</span> <span class="token class-name">MockMvc</span> mvc<span class="token punctuation">;</span>

    <span class="token annotation punctuation">@MockBean</span>
    <span class="token keyword">private</span> <span class="token class-name">UserVehicleService</span> userVehicleService<span class="token punctuation">;</span>

    <span class="token annotation punctuation">@Test</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">testExample</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">Exception</span> <span class="token punctuation">&#123;</span>
        <span class="token function">given</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>userVehicleService<span class="token punctuation">.</span><span class="token function">getVehicleDetails</span><span class="token punctuation">(</span><span class="token string">"sboot"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">willReturn</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">VehicleDetails</span><span class="token punctuation">(</span><span class="token string">"Honda"</span><span class="token punctuation">,</span> <span class="token string">"Civic"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>mvc<span class="token punctuation">.</span><span class="token function">perform</span><span class="token punctuation">(</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">"/sboot/vehicle"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">accept</span><span class="token punctuation">(</span><span class="token class-name">MediaType</span><span class="token punctuation">.</span>TEXT_PLAIN<span class="token punctuation">)</span><span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">andExpect</span><span class="token punctuation">(</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">isOk</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">andExpect</span><span class="token punctuation">(</span><span class="token function">content</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">string</span><span class="token punctuation">(</span><span class="token string">"Honda Civic"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>SpringBoot对各种单元测试的场景支持的比较全，更多的示例可直接在<a target="_blank" rel="noopener" href="https://docs.spring.io/spring-boot/docs/2.0.2.RELEASE/reference/htmlsingle/#boot-features-testing">Spiring Boot Test 官方指南</a>中去查看，这里就不再一一列举了。</p>
<h3 id="5-JaCoCo"><a href="#5-JaCoCo" class="headerlink" title="5. JaCoCo"></a>5. JaCoCo</h3><p>在做单元测试时，代码覆盖率常常被拿来作为衡量测试好坏的指标，甚至，用代码覆盖率来考核测试任务完成情况，比如，代码覆盖率必须达到80％或 90％。<br>目前Java常用覆盖率工具<a target="_blank" rel="noopener" href="https://www.atlassian.com/software/clover">clover</a>、<a target="_blank" rel="noopener" href="http://www.eclemma.org/jacoco/">Jacoco</a>和<a target="_blank" rel="noopener" href="https://cobertura.github.io/cobertura/">Cobertura</a>等。关于这些代码覆盖率工具的对比可参看<a target="_blank" rel="noopener" href="https://confluence.atlassian.com/clover/comparison-of-code-coverage-tools-681706101.html?_ga=2.7075890.1253300005.1526228675-143716968.1468426926">这里</a>。这里我们就选取 Jacoco 来作为代码覆盖率工具来做介绍。</p>
<p>Jacoco 是一个开源的覆盖率工具。Jacoco 可以嵌入到Ant 、Maven中，并提供了 Eclipse、IDEA 插件,也可以使用Java Agent技术监控Java程序。很多第三方的工具提供了对 Jacoco 的集成，如sonar、Jenkins。</p>
<p>Jacoco与Maven的集成很简单，只需要在plugins中添加如下插件即可。</p>
<pre class="line-numbers language-xml" data-language="xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>plugin</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>groupId</span><span class="token punctuation">></span></span>org.jacoco<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>groupId</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>artifactId</span><span class="token punctuation">></span></span>jacoco-maven-plugin<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>artifactId</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>version</span><span class="token punctuation">></span></span>0.7.7.201606060606<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>version</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>configuration</span><span class="token punctuation">></span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>destFile</span><span class="token punctuation">></span></span>target/coverage-reports/jacoco-unit.exec<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>destFile</span><span class="token punctuation">></span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dataFile</span><span class="token punctuation">></span></span>target/coverage-reports/jacoco-unit.exec<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dataFile</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>configuration</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>executions</span><span class="token punctuation">></span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>execution</span><span class="token punctuation">></span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>id</span><span class="token punctuation">></span></span>jacoco-initialize<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>id</span><span class="token punctuation">></span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>goals</span><span class="token punctuation">></span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>goal</span><span class="token punctuation">></span></span>prepare-agent<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>goal</span><span class="token punctuation">></span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>goals</span><span class="token punctuation">></span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>execution</span><span class="token punctuation">></span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>execution</span><span class="token punctuation">></span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>id</span><span class="token punctuation">></span></span>jacoco-site<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>id</span><span class="token punctuation">></span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>phase</span><span class="token punctuation">></span></span>package<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>phase</span><span class="token punctuation">></span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>goals</span><span class="token punctuation">></span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>goal</span><span class="token punctuation">></span></span>report<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>goal</span><span class="token punctuation">></span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>goals</span><span class="token punctuation">></span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>execution</span><span class="token punctuation">></span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>executions</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>plugin</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>做单元测试时，测试覆盖率是不是越高代表代码质量越好呢？Martin Fowler（重构那本书的作者）曾经写过一篇博客来讨论这个问题，他指出：<strong>把测试覆盖作为质量目标没有任何意义，而我们应该把它作为一种发现未被测试覆盖的代码的手段</strong>。</p>
<p>所以，代码覆盖率统计是用来发现没有被测试覆盖的代码；代码覆盖率统计不能完全用来衡量代码质量。</p>
<hr>
<p>参考资料</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://blog.csdn.net/vincetest/article/details/1378507">单元测试大揭密</a></li>
<li><a target="_blank" rel="noopener" href="https://www.yiibai.com/junit/">JUnit教程</a></li>
<li><a target="_blank" rel="noopener" href="http://sjyuan.cc/junit5/user-guide-cn/">JUnit5用户指南中文版</a></li>
<li><a target="_blank" rel="noopener" href="https://blog.csdn.net/bboyfeiyu/article/details/52127551">Mockito中文文档</a></li>
<li><a target="_blank" rel="noopener" href="https://github.com/yangyubo/zh-unit-testing-guidelines/blob/master/readme.rst">单元测试准则</a></li>
</ul>

                
            </div>
            <hr/>

            

    <div class="reprint" id="reprint-statement">
        
            <div class="reprint__author">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-user">
                        文章作者:
                    </i>
                </span>
                <span class="reprint-info">
                    <a href="/about" rel="external nofollow noreferrer">blinkfox</a>
                </span>
            </div>
            <div class="reprint__type">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-link">
                        文章链接:
                    </i>
                </span>
                <span class="reprint-info">
                    <a href="https://blinkfox.github.io/2018/11/15/hou-duan/java/dan-yuan-ce-shi-zhi-nan/">https://blinkfox.github.io/2018/11/15/hou-duan/java/dan-yuan-ce-shi-zhi-nan/</a>
                </span>
            </div>
            <div class="reprint__notice">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-copyright">
                        版权声明:
                    </i>
                </span>
                <span class="reprint-info">
                    本博客所有文章除特別声明外，均采用
                    <a href="https://creativecommons.org/licenses/by/4.0/deed.zh" rel="external nofollow noreferrer" target="_blank">CC BY 4.0</a>
                    许可协议。转载请注明来源
                    <a href="/about" target="_blank">blinkfox</a>
                    !
                </span>
            </div>
        
    </div>

    <script async defer>
      document.addEventListener("copy", function (e) {
        let toastHTML = '<span>复制成功，请遵循本文的转载规则</span><button class="btn-flat toast-action" onclick="navToReprintStatement()" style="font-size: smaller">查看</a>';
        M.toast({html: toastHTML})
      });

      function navToReprintStatement() {
        $("html, body").animate({scrollTop: $("#reprint-statement").offset().top - 80}, 800);
      }
    </script>



            <div class="tag_share" style="display: block;">
                <div class="post-meta__tag-list" style="display: inline-block;">
                    
                        <div class="article-tag">
                            
                                <a href="/tags/Java/">
                                    <span class="chip bg-color">Java</span>
                                </a>
                            
                                <a href="/tags/%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/">
                                    <span class="chip bg-color">单元测试</span>
                                </a>
                            
                        </div>
                    
                </div>
                <div class="post_share" style="zoom: 80%; width: fit-content; display: inline-block; float: right; margin: -0.15rem 0;">
                    <link rel="stylesheet" type="text/css" href="/libs/share/css/share.min.css">
<div id="article-share">

    
    <div class="social-share" data-sites="twitter,facebook,google,qq,qzone,wechat,weibo,douban,linkedin" data-wechat-qrcode-helper="<p>微信扫一扫即可分享！</p>"></div>
    <script src="/libs/share/js/social-share.min.js"></script>
    

    

</div>

                </div>
            </div>
            
                <style>
    #reward {
        margin: 40px 0;
        text-align: center;
    }

    #reward .reward-link {
        font-size: 1.4rem;
        line-height: 38px;
    }

    #reward .btn-floating:hover {
        box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2), 0 5px 15px rgba(0, 0, 0, 0.2);
    }

    #rewardModal {
        width: 320px;
        height: 350px;
    }

    #rewardModal .reward-title {
        margin: 15px auto;
        padding-bottom: 5px;
    }

    #rewardModal .modal-content {
        padding: 10px;
    }

    #rewardModal .close {
        position: absolute;
        right: 15px;
        top: 15px;
        color: rgba(0, 0, 0, 0.5);
        font-size: 1.3rem;
        line-height: 20px;
        cursor: pointer;
    }

    #rewardModal .close:hover {
        color: #ef5350;
        transform: scale(1.3);
        -moz-transform:scale(1.3);
        -webkit-transform:scale(1.3);
        -o-transform:scale(1.3);
    }

    #rewardModal .reward-tabs {
        margin: 0 auto;
        width: 210px;
    }

    .reward-tabs .tabs {
        height: 38px;
        margin: 10px auto;
        padding-left: 0;
    }

    .reward-content ul {
        padding-left: 0 !important;
    }

    .reward-tabs .tabs .tab {
        height: 38px;
        line-height: 38px;
    }

    .reward-tabs .tab a {
        color: #fff;
        background-color: #ccc;
    }

    .reward-tabs .tab a:hover {
        background-color: #ccc;
        color: #fff;
    }

    .reward-tabs .wechat-tab .active {
        color: #fff !important;
        background-color: #22AB38 !important;
    }

    .reward-tabs .alipay-tab .active {
        color: #fff !important;
        background-color: #019FE8 !important;
    }

    .reward-tabs .reward-img {
        width: 210px;
        height: 210px;
    }
</style>

<div id="reward">
    <a href="#rewardModal" class="reward-link modal-trigger btn-floating btn-medium waves-effect waves-light red">赏</a>

    <!-- Modal Structure -->
    <div id="rewardModal" class="modal">
        <div class="modal-content">
            <a class="close modal-close"><i class="fas fa-times"></i></a>
            <h4 class="reward-title">你的赏识是我前进的动力</h4>
            <div class="reward-content">
                <div class="reward-tabs">
                    <ul class="tabs row">
                        <li class="tab col s6 alipay-tab waves-effect waves-light"><a href="#alipay">支付宝</a></li>
                        <li class="tab col s6 wechat-tab waves-effect waves-light"><a href="#wechat">微 信</a></li>
                    </ul>
                    <div id="alipay">
                        <img src="/medias/reward/alipay.jpg" class="reward-img" alt="支付宝打赏二维码">
                    </div>
                    <div id="wechat">
                        <img src="/medias/reward/wechat.png" class="reward-img" alt="微信打赏二维码">
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

<script>
    $(function () {
        $('.tabs').tabs();
    });
</script>

            
        </div>
    </div>

    

    

    

    

    

    

    

    

<article id="prenext-posts" class="prev-next articles">
    <div class="row article-row">
        
        <div class="article col s12 m6" data-aos="fade-up">
            <div class="article-badge left-badge text-color">
                <i class="fas fa-chevron-left"></i>&nbsp;上一篇</div>
            <div class="card">
                <a href="/2018/11/18/ruan-jian-gong-ju/cpu-duo-ji-huan-cun/">
                    <div class="card-image">
                        
                        <img src="https://statics.sh1a.qingstor.com/2018/11/18/cpu.jpg" class="responsive-img" alt="CPU多级缓存">
                        
                        <span class="card-title">CPU多级缓存</span>
                    </div>
                </a>
                <div class="card-content article-content">
                    <div class="summary block-with-text">
                        
                            
                        
                    </div>
                    <div class="publish-info">
                        <span class="publish-date">
                            <i class="far fa-clock fa-fw icon-date"></i>2018-11-18
                        </span>
                        <span class="publish-author">
                            
                            <i class="fas fa-bookmark fa-fw icon-category"></i>
                            
                            <a href="/categories/%E8%BD%AF%E4%BB%B6%E5%B7%A5%E5%85%B7/" class="post-category">
                                    软件工具
                                </a>
                            
                            
                        </span>
                    </div>
                </div>
                
                <div class="card-action article-tags">
                    
                    <a href="/tags/CPU%E7%BC%93%E5%AD%98/">
                        <span class="chip bg-color">CPU缓存</span>
                    </a>
                    
                </div>
                
            </div>
        </div>
        
        
        <div class="article col s12 m6" data-aos="fade-up">
            <div class="article-badge right-badge text-color">
                下一篇&nbsp;<i class="fas fa-chevron-right"></i>
            </div>
            <div class="card">
                <a href="/2018/11/14/hou-duan/java/java8-xin-te-xing-ji-shi-yong-er/">
                    <div class="card-image">
                        
                        <img src="https://statics.sh1a.qingstor.com/2018/11/13/java8-2.jpg" class="responsive-img" alt="Java8新特性及使用(二)">
                        
                        <span class="card-title">Java8新特性及使用(二)</span>
                    </div>
                </a>
                <div class="card-content article-content">
                    <div class="summary block-with-text">
                        
                            
                        
                    </div>
                    <div class="publish-info">
                            <span class="publish-date">
                                <i class="far fa-clock fa-fw icon-date"></i>2018-11-14
                            </span>
                        <span class="publish-author">
                            
                            <i class="fas fa-bookmark fa-fw icon-category"></i>
                            
                            <a href="/categories/%E5%90%8E%E7%AB%AF/" class="post-category">
                                    后端
                                </a>
                            
                            
                        </span>
                    </div>
                </div>
                
                <div class="card-action article-tags">
                    
                    <a href="/tags/Java/">
                        <span class="chip bg-color">Java</span>
                    </a>
                    
                </div>
                
            </div>
        </div>
        
    </div>
</article>

</div>



<!-- 代码块功能依赖 -->
<script type="text/javascript" src="/libs/codeBlock/codeBlockFuction.js"></script>

<!-- 代码语言 -->

<script type="text/javascript" src="/libs/codeBlock/codeLang.js"></script>


<!-- 代码块复制 -->

<script type="text/javascript" src="/libs/codeBlock/codeCopy.js"></script>


<!-- 代码块收缩 -->

<script type="text/javascript" src="/libs/codeBlock/codeShrink.js"></script>


    </div>
    <div id="toc-aside" class="expanded col l3 hide-on-med-and-down">
        <div class="toc-widget card" style="background-color: white;">
            <div class="toc-title"><i class="far fa-list-alt"></i>&nbsp;&nbsp;目录</div>
            <div id="toc-content"></div>
        </div>
    </div>
</div>

<!-- TOC 悬浮按钮. -->

<div id="floating-toc-btn" class="hide-on-med-and-down">
    <a class="btn-floating btn-large bg-color">
        <i class="fas fa-list-ul"></i>
    </a>
</div>


<script src="/libs/tocbot/tocbot.min.js"></script>
<script>
    $(function () {
        tocbot.init({
            tocSelector: '#toc-content',
            contentSelector: '#articleContent',
            headingsOffset: -($(window).height() * 0.4 - 45),
            collapseDepth: Number('0'),
            headingSelector: 'h2, h3, h4'
        });

        // modify the toc link href to support Chinese.
        let i = 0;
        let tocHeading = 'toc-heading-';
        $('#toc-content a').each(function () {
            $(this).attr('href', '#' + tocHeading + (++i));
        });

        // modify the heading title id to support Chinese.
        i = 0;
        $('#articleContent').children('h2, h3, h4').each(function () {
            $(this).attr('id', tocHeading + (++i));
        });

        // Set scroll toc fixed.
        let tocHeight = parseInt($(window).height() * 0.4 - 64);
        let $tocWidget = $('.toc-widget');
        $(window).scroll(function () {
            let scroll = $(window).scrollTop();
            /* add post toc fixed. */
            if (scroll > tocHeight) {
                $tocWidget.addClass('toc-fixed');
            } else {
                $tocWidget.removeClass('toc-fixed');
            }
        });

        
        /* 修复文章卡片 div 的宽度. */
        let fixPostCardWidth = function (srcId, targetId) {
            let srcDiv = $('#' + srcId);
            if (srcDiv.length === 0) {
                return;
            }

            let w = srcDiv.width();
            if (w >= 450) {
                w = w + 21;
            } else if (w >= 350 && w < 450) {
                w = w + 18;
            } else if (w >= 300 && w < 350) {
                w = w + 16;
            } else {
                w = w + 14;
            }
            $('#' + targetId).width(w);
        };

        // 切换TOC目录展开收缩的相关操作.
        const expandedClass = 'expanded';
        let $tocAside = $('#toc-aside');
        let $mainContent = $('#main-content');
        $('#floating-toc-btn .btn-floating').click(function () {
            if ($tocAside.hasClass(expandedClass)) {
                $tocAside.removeClass(expandedClass).hide();
                $mainContent.removeClass('l9');
            } else {
                $tocAside.addClass(expandedClass).show();
                $mainContent.addClass('l9');
            }
            fixPostCardWidth('artDetail', 'prenext-posts');
        });
        
    });
</script>

    

</main>




    <footer class="page-footer bg-color">
    
        <link rel="stylesheet" href="/libs/aplayer/APlayer.min.css">
<style>
    .aplayer .aplayer-lrc p {
        
        font-size: 12px;
        font-weight: 700;
        line-height: 16px !important;
    }

    .aplayer .aplayer-lrc p.aplayer-lrc-current {
        
        font-size: 15px;
        color: #42b983;
    }

    
    .aplayer.aplayer-fixed.aplayer-narrow .aplayer-body {
        left: -66px !important;
    }

    .aplayer.aplayer-fixed.aplayer-narrow .aplayer-body:hover {
        left: 0px !important;
    }

    
</style>
<div class="">
    
    <div class="row">
        <meting-js class="col l8 offset-l2 m10 offset-m1 s12"
                   server="netease"
                   type="playlist"
                   id="503838841"
                   fixed='true'
                   autoplay='false'
                   theme='#42b983'
                   loop='all'
                   order='random'
                   preload='auto'
                   volume='0.7'
                   list-folded='true'
        >
        </meting-js>
    </div>
</div>

<script src="/libs/aplayer/APlayer.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/meting@2/dist/Meting.min.js"></script>

    

    <div class="container row center-align" style="margin-bottom: 15px !important;">
        <div class="col s12 m8 l8 copy-right">
            Copyright&nbsp;&copy;
            
                <span id="year">2018-2020</span>
            
            <span id="year">2018</span>
            <a href="/about" target="_blank">blinkfox</a>
            |&nbsp;Powered by&nbsp;<a href="https://hexo.io/" target="_blank">Hexo</a>
            |&nbsp;Theme&nbsp;<a href="https://github.com/blinkfox/hexo-theme-matery" target="_blank">Matery</a>
            <br>
            
            &nbsp;<i class="fas fa-chart-area"></i>&nbsp;站点总字数:&nbsp;<span
                class="white-color">197k</span>&nbsp;字
            
            
            
            
            
            
            <span id="busuanzi_container_site_pv">
                |&nbsp;<i class="far fa-eye"></i>&nbsp;总访问量:&nbsp;<span id="busuanzi_value_site_pv"
                    class="white-color"></span>&nbsp;次
            </span>
            
            
            <span id="busuanzi_container_site_uv">
                |&nbsp;<i class="fas fa-users"></i>&nbsp;总访问人数:&nbsp;<span id="busuanzi_value_site_uv"
                    class="white-color"></span>&nbsp;人
            </span>
            
            <br>
            
            <span id="sitetime">载入运行时间...</span>
            <script>
                function siteTime() {
                    var seconds = 1000;
                    var minutes = seconds * 60;
                    var hours = minutes * 60;
                    var days = hours * 24;
                    var years = days * 365;
                    var today = new Date();
                    var startYear = "2018";
                    var startMonth = "09";
                    var startDate = "20";
                    var startHour = "0";
                    var startMinute = "0";
                    var startSecond = "0";
                    var todayYear = today.getFullYear();
                    var todayMonth = today.getMonth() + 1;
                    var todayDate = today.getDate();
                    var todayHour = today.getHours();
                    var todayMinute = today.getMinutes();
                    var todaySecond = today.getSeconds();
                    var t1 = Date.UTC(startYear, startMonth, startDate, startHour, startMinute, startSecond);
                    var t2 = Date.UTC(todayYear, todayMonth, todayDate, todayHour, todayMinute, todaySecond);
                    var diff = t2 - t1;
                    var diffYears = Math.floor(diff / years);
                    var diffDays = Math.floor((diff / days) - diffYears * 365);
                    var diffHours = Math.floor((diff - (diffYears * 365 + diffDays) * days) / hours);
                    var diffMinutes = Math.floor((diff - (diffYears * 365 + diffDays) * days - diffHours * hours) /
                        minutes);
                    var diffSeconds = Math.floor((diff - (diffYears * 365 + diffDays) * days - diffHours * hours -
                        diffMinutes * minutes) / seconds);
                    if (startYear == todayYear) {
                        document.getElementById("year").innerHTML = todayYear;
                        document.getElementById("sitetime").innerHTML = "本站已安全运行 " + diffDays + " 天 " + diffHours +
                            " 小时 " + diffMinutes + " 分钟 " + diffSeconds + " 秒";
                    } else {
                        document.getElementById("year").innerHTML = startYear + " - " + todayYear;
                        document.getElementById("sitetime").innerHTML = "本站已安全运行 " + diffYears + " 年 " + diffDays +
                            " 天 " + diffHours + " 小时 " + diffMinutes + " 分钟 " + diffSeconds + " 秒";
                    }
                }
                setInterval(siteTime, 1000);
            </script>
            
            <br>
            
        </div>
        <div class="col s12 m4 l4 social-link social-statis">
    <a href="https://github.com/blinkfox" class="tooltipped" target="_blank" data-tooltip="访问我的GitHub" data-position="top" data-delay="50">
        <i class="fab fa-github"></i>
    </a>



    <a href="mailto:1181062873@qq.com" class="tooltipped" target="_blank" data-tooltip="邮件联系我" data-position="top" data-delay="50">
        <i class="fas fa-envelope-open"></i>
    </a>







    <a href="tencent://AddContact/?fromId=50&fromSubId=1&subcmd=all&uin=1181062873" class="tooltipped" target="_blank" data-tooltip="QQ联系我: 1181062873" data-position="top" data-delay="50">
        <i class="fab fa-qq"></i>
    </a>







    <a href="/atom.xml" class="tooltipped" target="_blank" data-tooltip="RSS 订阅" data-position="top" data-delay="50">
        <i class="fas fa-rss"></i>
    </a>

</div>
    </div>
</footer>

<div class="progress-bar"></div>


    <!-- 搜索遮罩框 -->
<div id="searchModal" class="modal">
    <div class="modal-content">
        <div class="search-header">
            <span class="title"><i class="fas fa-search"></i>&nbsp;&nbsp;搜索</span>
            <input type="search" id="searchInput" name="s" placeholder="请输入搜索的关键字"
                   class="search-input">
        </div>
        <div id="searchResult"></div>
    </div>
</div>

<script type="text/javascript">
    $(function () {
        var searchFunc = function (path, search_id, content_id) {
            'use strict';
            $.ajax({
                url: path,
                dataType: "xml",
                success: function (xmlResponse) {
                    // get the contents from search data
                    var datas = $("entry", xmlResponse).map(function () {
                        return {
                            title: $("title", this).text(),
                            content: $("content", this).text(),
                            url: $("url", this).text()
                        };
                    }).get();
                    var $input = document.getElementById(search_id);
                    var $resultContent = document.getElementById(content_id);
                    $input.addEventListener('input', function () {
                        var str = '<ul class=\"search-result-list\">';
                        var keywords = this.value.trim().toLowerCase().split(/[\s\-]+/);
                        $resultContent.innerHTML = "";
                        if (this.value.trim().length <= 0) {
                            return;
                        }
                        // perform local searching
                        datas.forEach(function (data) {
                            var isMatch = true;
                            var data_title = data.title.trim().toLowerCase();
                            var data_content = data.content.trim().replace(/<[^>]+>/g, "").toLowerCase();
                            var data_url = data.url;
                            data_url = data_url.indexOf('/') === 0 ? data.url : '/' + data_url;
                            var index_title = -1;
                            var index_content = -1;
                            var first_occur = -1;
                            // only match artiles with not empty titles and contents
                            if (data_title !== '' && data_content !== '') {
                                keywords.forEach(function (keyword, i) {
                                    index_title = data_title.indexOf(keyword);
                                    index_content = data_content.indexOf(keyword);
                                    if (index_title < 0 && index_content < 0) {
                                        isMatch = false;
                                    } else {
                                        if (index_content < 0) {
                                            index_content = 0;
                                        }
                                        if (i === 0) {
                                            first_occur = index_content;
                                        }
                                    }
                                });
                            }
                            // show search results
                            if (isMatch) {
                                str += "<li><a href='" + data_url + "' class='search-result-title'>" + data_title + "</a>";
                                var content = data.content.trim().replace(/<[^>]+>/g, "");
                                if (first_occur >= 0) {
                                    // cut out 100 characters
                                    var start = first_occur - 20;
                                    var end = first_occur + 80;
                                    if (start < 0) {
                                        start = 0;
                                    }
                                    if (start === 0) {
                                        end = 100;
                                    }
                                    if (end > content.length) {
                                        end = content.length;
                                    }
                                    var match_content = content.substr(start, end);
                                    // highlight all keywords
                                    keywords.forEach(function (keyword) {
                                        var regS = new RegExp(keyword, "gi");
                                        match_content = match_content.replace(regS, "<em class=\"search-keyword\">" + keyword + "</em>");
                                    });

                                    str += "<p class=\"search-result\">" + match_content + "...</p>"
                                }
                                str += "</li>";
                            }
                        });
                        str += "</ul>";
                        $resultContent.innerHTML = str;
                    });
                }
            });
        };

        searchFunc('/search.xml', 'searchInput', 'searchResult');
    });
</script>

    <!-- 回到顶部按钮 -->
<div id="backTop" class="top-scroll">
    <a class="btn-floating btn-large waves-effect waves-light" href="#!">
        <i class="fas fa-arrow-up"></i>
    </a>
</div>


    <script src="/libs/materialize/materialize.min.js"></script>
    <script src="/libs/masonry/masonry.pkgd.min.js"></script>
    <script src="/libs/aos/aos.js"></script>
    <script src="/libs/scrollprogress/scrollProgress.min.js"></script>
    <script src="/libs/lightGallery/js/lightgallery-all.min.js"></script>
    <script src="/js/matery.js"></script>

    <!-- Baidu Analytics -->

    <!-- Baidu Push -->

<script>
    (function () {
        var bp = document.createElement('script');
        var curProtocol = window.location.protocol.split(':')[0];
        if (curProtocol === 'https') {
            bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
        } else {
            bp.src = 'http://push.zhanzhang.baidu.com/push.js';
        }
        var s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(bp, s);
    })();
</script>

    
    <script src="/libs/others/clicklove.js" async="async"></script>
    
    
    <script async src="/libs/others/busuanzi.pure.mini.js"></script>
    

    

    

	
    

    
    
    <script type="text/javascript" size="150" alpha='0.6'
        zIndex="-1" src="/libs/background/ribbon-refresh.min.js" async="async"></script>
    

    

    
    <script src="/libs/instantpage/instantpage.js" type="module"></script>
    

</body>

</html>
